diff --git a/feed_rss_created.xml b/feed_rss_created.xml index 2d3963d5..5598acdf 100644 --- a/feed_rss_created.xml +++ b/feed_rss_created.xml @@ -1 +1 @@ - CHANfiGEasier Configurationhttps://chanfig.danling.org/CHANfiG Contributorshttps://github.com/ZhiyuanChen/CHANfiGen Tue, 20 Aug 2024 09:37:31 -0000 Tue, 20 Aug 2024 09:37:31 -0000 1440 MkDocs RSS plugin - v1.15.0 About <h1>About</h1><p style="text-align: center;">Developed by DanLing on Earth</p><p>We are a community of developers, designers, and others from around the world who ...</p>https://chanfig.danling.org/about/ Mon, 29 Jul 2024 20:39:24 +0000CHANfiGhttps://chanfig.danling.org/about/ 关于 <h1>关于</h1><p style="text-align: center;">由丹灵在地球开发</p><p>我们是一个由开发者、设计人员和其他人员组成的社区,致力于让深度学习技术更加开放。</p><p>我们是一个由个体组成的社区,致力于推动深度学习的可能性边界。</p><p>我们对深度学习及其用户充满激情。</p><p>我们是丹灵。</p>https://chanfig.danling.org/zh/about/ Mon, 29 Jul 2024 20:39:24 +0000CHANfiGhttps://chanfig.danling.org/zh/about/ License <h1>GNU AFFERO GENERAL PUBLIC LICENSE</h1><p>Version 3, 19 November 2007</p><p>Copyright (C) 2007 Free Software Foundation, Inc.<a href="https://fsf.org/">https://fsf.org/</a></p><p>Everyone is permitted...</p>https://chanfig.danling.org/about/license/ Mon, 08 Jul 2024 05:11:39 +0000CHANfiGhttps://chanfig.danling.org/about/license/ Privacy Notice <p>!!! info "Last Revised Date"</p><pre><code>This notice was last updated on May 04, 2024.</code></pre><h1>Privacy Notice</h1><p>This privacy notice for DanLing Team (also known as DanLin...</p>https://chanfig.danling.org/about/privacy/ Mon, 08 Jul 2024 05:11:39 +0000CHANfiGhttps://chanfig.danling.org/about/privacy/ License <p>!!! warning "翻译"</p><pre><code>本文内容为翻译版本,旨在为用户提供方便。我们已经尽力确保翻译的准确性。但请注意,翻译内容可能包含错误,仅供参考。请以英文[原文](https://multimolecule.danling.org/about/license)为准。</code></pre><p>...</p>https://chanfig.danling.org/zh/about/license/ Mon, 08 Jul 2024 05:11:39 +0000CHANfiGhttps://chanfig.danling.org/zh/about/license/ Privacy Notice <p>!!! warning "翻译"</p><pre><code>本文内容为机器翻译版本,旨在为用户提供方便。我们已经尽力确保翻译的准确性。但请注意,翻译内容可能包含错误,仅供参考。请以英文[原文](https://chanfig.danling.org/about/privacy)为准。为满...</code></pre>https://chanfig.danling.org/zh/about/privacy/ Mon, 08 Jul 2024 05:11:39 +0000CHANfiGhttps://chanfig.danling.org/zh/about/privacy/ 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/ Home 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/ configclass Zhiyuan Chen <h1>configclass</h1><p>::: chanfig.configclasses</p>https://chanfig.danling.org/configclass/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/configclass/ 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/ Index 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/ \ No newline at end of file + CHANfiGEasier Configurationhttps://chanfig.danling.org/CHANfiG Contributorshttps://github.com/ZhiyuanChen/CHANfiGen Tue, 20 Aug 2024 12:51:13 -0000 Tue, 20 Aug 2024 12:51:13 -0000 1440 MkDocs RSS plugin - v1.15.0 About <h1>About</h1><p style="text-align: center;">Developed by DanLing on Earth</p><p>We are a community of developers, designers, and others from around the world who ...</p>https://chanfig.danling.org/about/ Mon, 29 Jul 2024 20:39:24 +0000CHANfiGhttps://chanfig.danling.org/about/ 关于 <h1>关于</h1><p style="text-align: center;">由丹灵在地球开发</p><p>我们是一个由开发者、设计人员和其他人员组成的社区,致力于让深度学习技术更加开放。</p><p>我们是一个由个体组成的社区,致力于推动深度学习的可能性边界。</p><p>我们对深度学习及其用户充满激情。</p><p>我们是丹灵。</p>https://chanfig.danling.org/zh/about/ Mon, 29 Jul 2024 20:39:24 +0000CHANfiGhttps://chanfig.danling.org/zh/about/ License <h1>GNU AFFERO GENERAL PUBLIC LICENSE</h1><p>Version 3, 19 November 2007</p><p>Copyright (C) 2007 Free Software Foundation, Inc.<a href="https://fsf.org/">https://fsf.org/</a></p><p>Everyone is permitted...</p>https://chanfig.danling.org/about/license/ Mon, 08 Jul 2024 05:11:39 +0000CHANfiGhttps://chanfig.danling.org/about/license/ Privacy Notice <p>!!! info "Last Revised Date"</p><pre><code>This notice was last updated on May 04, 2024.</code></pre><h1>Privacy Notice</h1><p>This privacy notice for DanLing Team (also known as DanLin...</p>https://chanfig.danling.org/about/privacy/ Mon, 08 Jul 2024 05:11:39 +0000CHANfiGhttps://chanfig.danling.org/about/privacy/ License <p>!!! warning "翻译"</p><pre><code>本文内容为翻译版本,旨在为用户提供方便。我们已经尽力确保翻译的准确性。但请注意,翻译内容可能包含错误,仅供参考。请以英文[原文](https://multimolecule.danling.org/about/license)为准。</code></pre><p>...</p>https://chanfig.danling.org/zh/about/license/ Mon, 08 Jul 2024 05:11:39 +0000CHANfiGhttps://chanfig.danling.org/zh/about/license/ Privacy Notice <p>!!! warning "翻译"</p><pre><code>本文内容为机器翻译版本,旨在为用户提供方便。我们已经尽力确保翻译的准确性。但请注意,翻译内容可能包含错误,仅供参考。请以英文[原文](https://chanfig.danling.org/about/privacy)为准。为满...</code></pre>https://chanfig.danling.org/zh/about/privacy/ Mon, 08 Jul 2024 05:11:39 +0000CHANfiGhttps://chanfig.danling.org/zh/about/privacy/ 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/ Home 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/ configclass Zhiyuan Chen <h1>configclass</h1><p>::: chanfig.configclasses</p>https://chanfig.danling.org/configclass/ Wed, 04 May 2022 00:00:00 +0000CHANfiGhttps://chanfig.danling.org/configclass/ 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/ Index 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/ \ No newline at end of file diff --git a/feed_rss_updated.xml b/feed_rss_updated.xml index a5d9361e..7c786673 100644 --- a/feed_rss_updated.xml +++ b/feed_rss_updated.xml @@ -1 +1 @@ - CHANfiGEasier Configurationhttps://chanfig.danling.org/CHANfiG Contributorshttps://github.com/ZhiyuanChen/CHANfiGen Tue, 20 Aug 2024 09:37:31 -0000 Tue, 20 Aug 2024 09:37:31 -0000 1440 MkDocs RSS plugin - v1.15.0 About <h1>About</h1><p style="text-align: center;">Developed by DanLing on Earth</p><p>We are a community of developers, designers, and others from around the world who ...</p>https://chanfig.danling.org/about/ Mon, 29 Jul 2024 20:39:24 +0000CHANfiGhttps://chanfig.danling.org/about/ Privacy Notice <p>!!! info "Last Revised Date"</p><pre><code>This notice was last updated on May 04, 2024.</code></pre><h1>Privacy Notice</h1><p>This privacy notice for DanLing Team (also known as DanLin...</p>https://chanfig.danling.org/about/privacy/ Mon, 29 Jul 2024 20:39:24 +0000CHANfiGhttps://chanfig.danling.org/about/privacy/ 关于 <h1>关于</h1><p style="text-align: center;">由丹灵在地球开发</p><p>我们是一个由开发者、设计人员和其他人员组成的社区,致力于让深度学习技术更加开放。</p><p>我们是一个由个体组成的社区,致力于推动深度学习的可能性边界。</p><p>我们对深度学习及其用户充满激情。</p><p>我们是丹灵。</p>https://chanfig.danling.org/zh/about/ Mon, 29 Jul 2024 20:39:24 +0000CHANfiGhttps://chanfig.danling.org/zh/about/ Privacy Notice <p>!!! warning "翻译"</p><pre><code>本文内容为机器翻译版本,旨在为用户提供方便。我们已经尽力确保翻译的准确性。但请注意,翻译内容可能包含错误,仅供参考。请以英文[原文](https://chanfig.danling.org/about/privacy)为准。为满...</code></pre>https://chanfig.danling.org/zh/about/privacy/ Mon, 29 Jul 2024 20:39:24 +0000CHANfiGhttps://chanfig.danling.org/zh/about/privacy/ License <h1>GNU AFFERO GENERAL PUBLIC LICENSE</h1><p>Version 3, 19 November 2007</p><p>Copyright (C) 2007 Free Software Foundation, Inc.<a href="https://fsf.org/">https://fsf.org/</a></p><p>Everyone is permitted...</p>https://chanfig.danling.org/about/license/ Mon, 08 Jul 2024 05:11:39 +0000CHANfiGhttps://chanfig.danling.org/about/license/ License <p>!!! warning "翻译"</p><pre><code>本文内容为翻译版本,旨在为用户提供方便。我们已经尽力确保翻译的准确性。但请注意,翻译内容可能包含错误,仅供参考。请以英文[原文](https://multimolecule.danling.org/about/license)为准。</code></pre><p>...</p>https://chanfig.danling.org/zh/about/license/ Mon, 08 Jul 2024 05:11:39 +0000CHANfiGhttps://chanfig.danling.org/zh/about/license/ Registry Zhiyuan Chen <h1>Registry</h1><p>::: chanfig.registry</p>https://chanfig.danling.org/registry/ Thu, 28 Mar 2024 11:05:48 +0000CHANfiGhttps://chanfig.danling.org/registry/ Registry Zhiyuan Chen <h1>Registry</h1><p>::: chanfig.registry</p>https://chanfig.danling.org/zh/registry/ Thu, 28 Mar 2024 11:05:48 +0000CHANfiGhttps://chanfig.danling.org/zh/registry/ configclass Zhiyuan Chen <h1>configclass</h1><p>::: chanfig.configclasses</p>https://chanfig.danling.org/configclass/ Fri, 08 Mar 2024 08:01:02 +0000CHANfiGhttps://chanfig.danling.org/configclass/ configclass Zhiyuan Chen <h1>configclass</h1><p>::: chanfig.configclasses</p>https://chanfig.danling.org/zh/configclass/ Fri, 08 Mar 2024 08:01:02 +0000CHANfiGhttps://chanfig.danling.org/zh/configclass/ 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/ Home 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/ \ No newline at end of file + CHANfiGEasier Configurationhttps://chanfig.danling.org/CHANfiG Contributorshttps://github.com/ZhiyuanChen/CHANfiGen Tue, 20 Aug 2024 12:51:13 -0000 Tue, 20 Aug 2024 12:51:13 -0000 1440 MkDocs RSS plugin - v1.15.0 About <h1>About</h1><p style="text-align: center;">Developed by DanLing on Earth</p><p>We are a community of developers, designers, and others from around the world who ...</p>https://chanfig.danling.org/about/ Mon, 29 Jul 2024 20:39:24 +0000CHANfiGhttps://chanfig.danling.org/about/ Privacy Notice <p>!!! info "Last Revised Date"</p><pre><code>This notice was last updated on May 04, 2024.</code></pre><h1>Privacy Notice</h1><p>This privacy notice for DanLing Team (also known as DanLin...</p>https://chanfig.danling.org/about/privacy/ Mon, 29 Jul 2024 20:39:24 +0000CHANfiGhttps://chanfig.danling.org/about/privacy/ 关于 <h1>关于</h1><p style="text-align: center;">由丹灵在地球开发</p><p>我们是一个由开发者、设计人员和其他人员组成的社区,致力于让深度学习技术更加开放。</p><p>我们是一个由个体组成的社区,致力于推动深度学习的可能性边界。</p><p>我们对深度学习及其用户充满激情。</p><p>我们是丹灵。</p>https://chanfig.danling.org/zh/about/ Mon, 29 Jul 2024 20:39:24 +0000CHANfiGhttps://chanfig.danling.org/zh/about/ Privacy Notice <p>!!! warning "翻译"</p><pre><code>本文内容为机器翻译版本,旨在为用户提供方便。我们已经尽力确保翻译的准确性。但请注意,翻译内容可能包含错误,仅供参考。请以英文[原文](https://chanfig.danling.org/about/privacy)为准。为满...</code></pre>https://chanfig.danling.org/zh/about/privacy/ Mon, 29 Jul 2024 20:39:24 +0000CHANfiGhttps://chanfig.danling.org/zh/about/privacy/ License <h1>GNU AFFERO GENERAL PUBLIC LICENSE</h1><p>Version 3, 19 November 2007</p><p>Copyright (C) 2007 Free Software Foundation, Inc.<a href="https://fsf.org/">https://fsf.org/</a></p><p>Everyone is permitted...</p>https://chanfig.danling.org/about/license/ Mon, 08 Jul 2024 05:11:39 +0000CHANfiGhttps://chanfig.danling.org/about/license/ License <p>!!! warning "翻译"</p><pre><code>本文内容为翻译版本,旨在为用户提供方便。我们已经尽力确保翻译的准确性。但请注意,翻译内容可能包含错误,仅供参考。请以英文[原文](https://multimolecule.danling.org/about/license)为准。</code></pre><p>...</p>https://chanfig.danling.org/zh/about/license/ Mon, 08 Jul 2024 05:11:39 +0000CHANfiGhttps://chanfig.danling.org/zh/about/license/ Registry Zhiyuan Chen <h1>Registry</h1><p>::: chanfig.registry</p>https://chanfig.danling.org/registry/ Thu, 28 Mar 2024 11:05:48 +0000CHANfiGhttps://chanfig.danling.org/registry/ Registry Zhiyuan Chen <h1>Registry</h1><p>::: chanfig.registry</p>https://chanfig.danling.org/zh/registry/ Thu, 28 Mar 2024 11:05:48 +0000CHANfiGhttps://chanfig.danling.org/zh/registry/ configclass Zhiyuan Chen <h1>configclass</h1><p>::: chanfig.configclasses</p>https://chanfig.danling.org/configclass/ Fri, 08 Mar 2024 08:01:02 +0000CHANfiGhttps://chanfig.danling.org/configclass/ configclass Zhiyuan Chen <h1>configclass</h1><p>::: chanfig.configclasses</p>https://chanfig.danling.org/zh/configclass/ Fri, 08 Mar 2024 08:01:02 +0000CHANfiGhttps://chanfig.danling.org/zh/configclass/ 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/ Home 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/ \ No newline at end of file diff --git a/flat_dict/index.html b/flat_dict/index.html index df242e3f..d8b274af 100644 --- a/flat_dict/index.html +++ b/flat_dict/index.html @@ -2904,7 +2904,8 @@

FlatDict1529 1530 1531 -1532
class FlatDict(dict, metaclass=Dict):
+1532
+1533
class FlatDict(dict, metaclass=Dict):
     r"""
     `FlatDict` with attribute-style access.
 
@@ -3185,1139 +3186,1140 @@ 

FlatDict try: if name in self.__dict__: return self.__dict__[name] - if name in self.__class__.__dict__: - return self.__class__.__dict__[name] - return super().getattr(name, default) # type: ignore[misc] - except AttributeError: - if default is not Null: - return default - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") from None - - def setattr(self, name: str, value: Any) -> None: - r""" - Set attribute of `FlatDict`. - - Note that it won't alter values in `FlatDict`. - - Args: - name: - value: - - Warns: - RuntimeWarning: If name already exists in `FlatDict`. - - Examples: - >>> d = FlatDict() - >>> d.setattr('attr', 'value') - >>> d.getattr('attr') - 'value' - >>> d.set('d', 1013) - >>> d.setattr('d', 1031) # RuntimeWarning: d already exists in FlatDict. - >>> d.get('d') - 1013 - >>> d.d - 1013 - >>> d.getattr('d') - 1031 - """ - - if name in self: - warn( - f"{name} already exists in {self.__class__.__name__}.\n" - f"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.", - RuntimeWarning, - ) - self.__dict__[name] = value - - def delattr(self, name: str) -> None: - r""" - Delete attribute of `FlatDict`. - - Note that it won't delete values in `FlatDict`. - - Args: - name: - - Examples: - >>> d = FlatDict() - >>> d.setattr('name', 'chang') - >>> d.getattr('name') - 'chang' - >>> d.delattr('name') - >>> d.getattr('name') - Traceback (most recent call last): - AttributeError: 'FlatDict' object has no attribute 'name' - """ - - del self.__dict__[name] - - def hasattr(self, name: str) -> bool: - r""" - Determine if an attribute exists in `FlatDict`. - - Args: - name: - - Returns: - (bool): - - Examples: - >>> d = FlatDict() - >>> d.setattr('name', 'chang') - >>> d.hasattr('name') - True - >>> d.delattr('name') - >>> d.hasattr('name') - False - """ - - try: - if name in self.__dict__ or name in self.__class__.__dict__: - return True - return super().hasattr(name) # type: ignore[misc] - except AttributeError: - return False - - def dict(self, flatten: bool = False) -> Mapping | Sequence | Set: - r""" - Convert `FlatDict` to other `Mapping`. - - Args: - flatten: Whether to flatten [`NestedDict`][chanfig.NestedDict]. - - Returns: - (Mapping): - - See Also: - [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`. - - **Alias**: - - + `to_dict` - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.dict() - {'a': 1, 'b': 2, 'c': 3} - """ - - return to_dict(self, flatten) - - def to_dict(self, flatten: bool = False) -> Mapping | Sequence | Set: - r""" - Alias of [`dict`][chanfig.FlatDict.dict]. - """ - - return self.dict(flatten) - - @classmethod - def from_dict(cls, obj: Mapping | Sequence) -> Any: # pylint: disable=R0911 - r""" - Convert `Mapping` or `Sequence` to `FlatDict`. - - Examples: - >>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3}) - FlatDict( - ('a'): 1 - ('b'): 2 - ('c'): 3 - ) - >>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)]) - FlatDict( - ('a'): 1 - ('b'): 2 - ('c'): 3 - ) - >>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}]) - [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)] - >>> FlatDict.from_dict({1, 2, 3}) - Traceback (most recent call last): - TypeError: Expected Mapping or Sequence, but got <class 'set'>. - """ - - if obj is None: - return cls() - if issubclass(cls, FlatDict): - cls = cls.empty # type: ignore[assignment] # pylint: disable=W0642 - if isinstance(obj, Mapping): - return cls(obj) - if isinstance(obj, Sequence): - try: - return cls(obj) - except ValueError: - return [cls(json) for json in obj] - raise TypeError(f"Expected Mapping or Sequence, but got {type(obj)}.") - - def sort(self, key: Callable | None = None, reverse: bool = False) -> Self: - r""" - Sort `FlatDict`. - - Returns: - (FlatDict): - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.sort().dict() - {'a': 1, 'b': 2, 'c': 3} - >>> d = FlatDict(b=2, c=3, a=1) - >>> d.sort().dict() - {'a': 1, 'b': 2, 'c': 3} - >>> a = [1] - >>> d = FlatDict(z=0, a=a) - >>> a.append(2) - >>> d.sort().dict() - {'a': [1, 2], 'z': 0} - """ - - items = sorted(self.items(), key=key, reverse=reverse) - self.clear() - for k, v in items: # pylint: disable=C0103 - self[k] = v - return self - - def interpolate( # pylint: disable=R0912 - self, use_variable: bool = True, interpolators: MutableMapping | None = None, unsafe_eval: bool = False - ) -> Self: - r""" - Perform Variable interpolation. - - Variable interpolation allows you to set the value of one key to be the value of another key easily. - - Args: - use_variable: Whether to convert values to `Variable` objects. - interpolators: Mapping contains values for interpolation. Defaults to `self`. - unsafe_eval: Whether to evaluate interpolated values. - - Raises: - 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: - >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}") - >>> d.dict() - {'a': 1, 'b': '${a}', 'c': '${a}.${b}'} - >>> d.interpolate(unsafe_eval=True).dict() - {'a': 1, 'b': 1, 'c': 1.1} - >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}") - >>> d.dict() - {'a': 1, 'b': '${a}', 'c': '${a}.${b}'} - >>> d.interpolate().dict() - {'a': 1, 'b': 1, 'c': '1.1'} - >>> isinstance(d.a, Variable) - True - >>> d.a += 1 - >>> d.dict() - {'a': 2, 'b': 2, 'c': '1.1'} - >>> d.a is d.b - True - >>> d.b is d.c - False - >>> d = FlatDict(a=1, b="${a}", c="${b}") - >>> d.dict() - {'a': 1, 'b': '${a}', 'c': '${b}'} - >>> d.interpolate(False).dict() - {'a': 1, 'b': 1, 'c': 1} - >>> isinstance(d.a, Variable) - False - >>> d.a += 1 - >>> d.dict() - {'a': 2, 'b': 1, 'c': 1} - >>> d = FlatDict(a=1, b="${b}", c="${b}") - >>> d.interpolate().dict() - Traceback (most recent call last): - ValueError: Cannot interpolate b to itself. - >>> d = FlatDict(a="${b}", b="${c}", c="${d}", d="${a}") - >>> d.interpolate().dict() - Traceback (most recent call last): - ValueError: Circular reference found: a->b->c->d->a. - >>> d = FlatDict(a=1, b="${a}", c="${d}") - >>> d.interpolate().dict() - Traceback (most recent call last): - ValueError: d is not found in FlatDict( - ('a'): '1' - ('b'): '${a}' - ('c'): '${d}' - ). - """ - # pylint: disable=C0103 - - interpolators = interpolators or self - placeholders: dict[str, list[str]] = {} - for key, value in self.all_items(): - if isinstance(value, list): - for v in value: - self.find_placeholders(key, v, placeholders) - elif isinstance(value, Mapping): - for v in value.values(): - self.find_placeholders(key, v, placeholders) - else: - self.find_placeholders(key, value, placeholders) - circular_references = find_circular_reference(placeholders) - if circular_references: - raise ValueError(f"Circular reference found: {'->'.join(circular_references)}.") - if use_variable: - placeholder_names = {i for j in placeholders.values() for i in j} - for name in list(placeholder_names.difference(placeholders.keys())): - if name not in interpolators: - raise ValueError(f"{name} is not found in {interpolators}.") - if not isinstance(interpolators[name], Variable): - interpolators[name] = Variable(interpolators[name]) - for key, value in placeholders.items(): - if isinstance(self[key], list): - for index, v in enumerate(self[key]): - self[key][index] = self.substitute(v, interpolators, value) - elif isinstance(self[key], Mapping): - for k, v in self[key].items(): - self[key][k] = self.substitute(v, interpolators, value) - else: - self[key] = self.substitute(self[key], interpolators, value) - if unsafe_eval and isinstance(self[key], str): - with suppress(SyntaxError): - self[key] = eval(self[key]) # pylint: disable=W0123 - return self - - @staticmethod - def find_placeholders(key, value, placeholders): - placeholder = find_placeholders(value) - if placeholder: - for index, name in enumerate(placeholder): - if name.startswith("."): - placeholder[index] = key.rsplit(".", 1)[0] + name - if key == name: - raise ValueError(f"Cannot interpolate {key} to itself.") - placeholders[key] = placeholder - - @staticmethod - def substitute(placeholder, interpolators, value): - try: - if len(value) == 1 and placeholder.startswith("${") and placeholder.endswith("}"): - return interpolators[value[0]] - return placeholder.replace("$", "").format(**interpolators) - except KeyError as exc: - raise ValueError(f"{exc} is not found in {interpolators}.") from None - - def merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> Self: - r""" - Merge `other` into `FlatDict`. - - Args: - *args: `Mapping` or `Sequence` to be merged. - overwrite: Whether to overwrite existing values. - **kwargs: `Mapping` to be merged. - - Returns: - self: - - **Alias**: - - + `union` - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> n = {'b': 'b', 'c': 'c', 'd': 'd'} - >>> d.merge(n).dict() - {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'} - >>> l = [('c', 3), ('d', 4)] - >>> d.merge(l).dict() - {'a': 1, 'b': 'b', 'c': 3, 'd': 4} - >>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict() # alias - {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'} - >>> d = FlatDict() - >>> d.merge({1: 1, 2: 2, 3:3}).dict() - {1: 1, 2: 2, 3: 3} - >>> d.merge(d.clone()).dict() - {1: 1, 2: 2, 3: 3} - >>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict() - {1: 1, 2: 2, 3: 3, 4: 4, 5: 5} - """ - - if len(args) == 1: - args = args[0] - if isinstance(args, (PathLike, str, bytes)): - args = self.load(args) # type: ignore[assignment] - warn( - "merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.", - PendingDeprecationWarning, - ) - self._merge(self, args, overwrite=overwrite) - elif len(args) > 1: - self._merge(self, args, overwrite=overwrite) - if kwargs: - self._merge(self, kwargs, overwrite=overwrite) - return self - - @staticmethod - def _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping: - if not that: - return this - if isinstance(that, Mapping): - that = that.items() - for key, value in that: - if key in this and isinstance(this[key], Mapping): - if isinstance(value, Mapping): - FlatDict._merge(this[key], value) - elif overwrite: - if isinstance(value, FlatDict): - this.set(key, value) - else: - this[key] = value - elif overwrite or key not in this: - this.set(key, value) - return this - - def union(self, *args: Any, **kwargs: Any) -> Self: - r""" - Alias of [`merge`][chanfig.FlatDict.merge]. - """ - return self.merge(*args, **kwargs) - - def merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> Self: - r""" - Merge content of `file` into `FlatDict`. - - Args: - file (File): - *args: Passed to [`load`][chanfig.FlatDict.load]. - **kwargs: Passed to [`load`][chanfig.FlatDict.load]. - - Returns: - self: - - Examples: - >>> d = FlatDict(a=1, b=1) - >>> d.merge_from_file("tests/test.yaml").dict() - {'a': 1, 'b': 2, 'c': 3} - """ - - return self.merge(self.load(file, *args, **kwargs)) - - def intersect(self, other: Mapping | Iterable | PathStr) -> Self: - r""" - Intersection of `FlatDict` and `other`. - - Args: - other (Mapping | Iterable | PathStr): - - Returns: - (FlatDict): - - **Alias**: - - + `inter` - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> n = {'b': 'b', 'c': 'c', 'd': 'd'} - >>> d.intersect(n).dict() - {} - >>> l = [('c', 3), ('d', 4)] - >>> d.intersect(l).dict() - {'c': 3} - >>> d.merge(l).intersect("tests/test.yaml").dict() - {'a': 1, 'b': 2, 'c': 3} - >>> d.intersect(1) - Traceback (most recent call last): - TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>. - >>> d.inter(FlatDict(b='b', c='c', d='d')).dict() # alias - {} - """ - - if isinstance(other, (PathLike, str, bytes)): - other = self.load(other) - if isinstance(other, (Mapping,)): - other = self.empty(other).items() - if not isinstance(other, Iterable): - raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.") - return self.empty(**{key: value for key, value in other if key in self and self[key] == value}) # type: ignore - - def inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self: - r""" - Alias of [`intersect`][chanfig.FlatDict.intersect]. - """ - return self.intersect(other, *args, **kwargs) - - def difference(self, other: Mapping | Iterable | PathStr) -> Self: - r""" - Difference between `FlatDict` and `other`. - - Args: - other: - - Returns: - (FlatDict): - - **Alias**: - - + `diff` - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> n = {'b': 'b', 'c': 'c', 'd': 'd'} - >>> d.difference(n).dict() - {'b': 'b', 'c': 'c', 'd': 'd'} - >>> l = [('c', 3), ('d', 4)] - >>> d.difference(l).dict() - {'d': 4} - >>> d.merge(l).difference("tests/test.yaml").dict() - {} - >>> d.difference(1) - Traceback (most recent call last): - TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>. - >>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict() # alias - {'b': 'b', 'c': 'c', 'd': 'd'} - """ - - if isinstance(other, (PathLike, str, bytes)): - other = self.load(other) - if isinstance(other, (Mapping,)): - other = self.empty(other).items() - if not isinstance(other, Iterable): - raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.") - return self.empty( - **{key: value for key, value in other if key not in self or self[key] != value} # type: ignore[misc] - ) - - def diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self: - r""" - Alias of [`difference`][chanfig.FlatDict.difference]. - """ - return self.difference(other, *args, **kwargs) - - def to(self, cls: str | TorchDevice | TorchDType) -> Self: # pragma: no cover - r""" - Convert values of `FlatDict` to target `cls`. - - Args: - cls (str | torch.device | torch.dtype): - - Returns: - self: - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.to(int) - Traceback (most recent call last): - TypeError: to() only support torch.dtype and torch.device, but got <class 'int'>. - """ - - # pylint: disable=C0103 - - if isinstance(cls, (str, TorchDevice, TorchDType)): - for k, v in self.all_items(): - if hasattr(v, "to"): - self[k] = v.to(cls) - return self - - raise TypeError(f"to() only support torch.dtype and torch.device, but got {cls}.") - - def cpu(self) -> Self: # pragma: no cover - r""" - Move all tensors to cpu. - - Returns: - self: - - Examples: - >>> import torch - >>> d = FlatDict(a=torch.tensor(1)) - >>> d.cpu().dict() # doctest: +SKIP - {'a': tensor(1, device='cpu')} - """ - - return self.to(TorchDevice("cpu")) - - def gpu(self) -> Self: # pragma: no cover - r""" - Move all tensors to gpu. - - Returns: - self: - - **Alias**: - - + `cuda` - - Examples: - >>> import torch - >>> d = FlatDict(a=torch.tensor(1)) - >>> d.gpu().dict() # doctest: +SKIP - {'a': tensor(1, device='cuda:0')} - >>> d.cuda().dict() # alias # doctest: +SKIP - {'a': tensor(1, device='cuda:0')} - """ - - return self.to(TorchDevice("cuda")) - - def cuda(self) -> Self: # pragma: no cover - r""" - Alias of [`gpu`][chanfig.FlatDict.gpu]. - """ - return self.gpu() - - def tpu(self) -> Self: # pragma: no cover - r""" - Move all tensors to tpu. - - Returns: - self: - - **Alias**: - - + `xla` - - Examples: - >>> import torch - >>> d = FlatDict(a=torch.tensor(1)) - >>> d.tpu().dict() # doctest: +SKIP - {'a': tensor(1, device='xla:0')} - >>> d.xla().dict() # alias # doctest: +SKIP - {'a': tensor(1, device='xla:0')} - """ - - return self.to(TorchDevice("xla")) - - def xla(self) -> Self: # pragma: no cover - r""" - Alias of [`tpu`][chanfig.FlatDict.tpu]. - """ - return self.tpu() - - def copy(self) -> Self: - r""" - Create a shallow copy of `FlatDict`. - - Returns: - (FlatDict): - - Examples: - >>> d = FlatDict(a=[]) - >>> d.setattr("name", "Chang") - >>> c = d.copy() - >>> c.dict() - {'a': []} - >>> d.a.append(1) - >>> c.dict() - {'a': [1]} - >>> c.getattr("name") - 'Chang' - """ - - return copy(self) - - def __deepcopy__(self, memo: Mapping | None = None) -> Self: - # pylint: disable=C0103 - - if memo is not None and id(self) in memo: - return memo[id(self)] - ret = self.empty() - ret.__dict__.update(deepcopy(self.__dict__)) - for k, v in self.items(): - if isinstance(v, FlatDict): - ret[k] = v.deepcopy(memo=memo) - else: - ret[k] = deepcopy(v) - return ret - - def deepcopy(self, memo: Mapping | None = None) -> Self: # pylint: disable=W0613 - r""" - Create a deep copy of `FlatDict`. - - Returns: - (FlatDict): - - **Alias**: - - + `clone` - - Examples: - >>> d = FlatDict(a=[]) - >>> d.setattr("name", "Chang") - >>> c = d.deepcopy() - >>> c.dict() - {'a': []} - >>> d.a.append(1) - >>> c.dict() - {'a': []} - >>> c.getattr("name") - 'Chang' - >>> d == d.clone() # alias - True - """ - - return deepcopy(self) - - def clone(self, memo: Mapping | None = None) -> Self: - r""" - Alias of [`deepcopy`][chanfig.FlatDict.deepcopy]. - """ - return self.deepcopy(memo=memo) - - def save( # pylint: disable=W1113 - self, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment] - ) -> None: - r""" - Save `FlatDict` to file. - - Raises: - ValueError: If save to `IO` and `method` is not specified. - TypeError: If save to unsupported extension. - - **Alias**: - - + `save` - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.save("tests/test.yaml") - >>> d.save("test.conf") - Traceback (most recent call last): - TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf. - >>> with open("test.yaml", "w") as f: - ... d.save(f) - Traceback (most recent call last): - ValueError: `method` must be specified when saving to IO. - """ - - if method is None: - if isinstance(file, (IOBase, IO)): - raise ValueError("`method` must be specified when saving to IO.") - method = splitext(file)[-1][1:] - extension = method.lower() - if extension in YAML: - return self.yaml(file=file, *args, **kwargs) # type: ignore[misc] # noqa: B026 - if extension in JSON: - return self.json(file=file, *args, **kwargs) # type: ignore[misc] # noqa: B026 - raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.") - - def dump( # pylint: disable=W1113 - self, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment] - ) -> None: - r""" - Alias of [`save`][chanfig.FlatDict.save]. - """ - return self.save(file, method, *args, **kwargs) - - @classmethod - def load( # pylint: disable=W1113 - cls, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment] - ) -> Self: - """ - Load `FlatDict` from file. - - Args: - file: File to load from. - method: File type, should be in `JSON` or `YAML`. - - Returns: - (FlatDict): - - Raises: - ValueError: If load from `IO` and `method` is not specified. - TypeError: If dump to unsupported extension. - - Examples: - >>> d = FlatDict.load("tests/test.yaml") - >>> d.dict() - {'a': 1, 'b': 2, 'c': 3} - >>> d.load("tests/test.conf") - Traceback (most recent call last): - TypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf. - >>> with open("tests/test.yaml") as f: - ... d.load(f) - Traceback (most recent call last): - ValueError: `method` must be specified when loading from IO. - """ - - if method is None: - if isinstance(file, (IOBase, IO)): - raise ValueError("`method` must be specified when loading from IO.") - method = splitext(file)[-1][1:] - extension = method.lower() - if extension in JSON: - return cls.from_json(file, *args, **kwargs) - if extension in YAML: - return cls.from_yaml(file, *args, **kwargs) - raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.") - - def json(self, file: File, *args: Any, **kwargs: Any) -> None: - r""" - 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: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.json("tests/test.json") - """ - - with self.open(file, mode="w") as fp: # pylint: disable=C0103 - fp.write(self.jsons(*args, **kwargs)) - - @classmethod - def from_json(cls, file: File, *args: Any, **kwargs: Any) -> Self: - r""" - 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: - (FlatDict): - - Examples: - >>> d = FlatDict.from_json('tests/test.json') - >>> d.dict() - {'a': 1, 'b': 2, 'c': 3} - """ - - with cls.open(file) as fp: # pylint: disable=C0103 - if isinstance(file, (IOBase, IO)): - return cls.from_jsons(fp.getvalue(), *args, **kwargs) # type: ignore[union-attr] - return cls.from_jsons(fp.read(), *args, **kwargs) - - def jsons(self, *args: Any, **kwargs: Any) -> str: - r""" - Dump `FlatDict` to json string. - - Returns: - (str): - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.jsons() - '{\n "a": 1,\n "b": 2,\n "c": 3\n}' - """ - - kwargs.setdefault("cls", JsonEncoder) - kwargs.setdefault("indent", self.getattr("indent", 2)) - return json_dumps(self.dict(), *args, **kwargs) - - @classmethod - def from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> Self: - r""" - Construct `FlatDict` from json string. - - Returns: - (FlatDict): - - Examples: - >>> FlatDict.from_jsons('{\n "a": 1,\n "b": 2,\n "c": 3\n}').dict() - {'a': 1, 'b': 2, 'c': 3} - >>> FlatDict.from_jsons('[["a", 1], ["b", 2], ["c", 3]]').dict() - {'a': 1, 'b': 2, 'c': 3} - >>> FlatDict.from_jsons('[{"a": 1}, {"b": 2}, {"c": 3}]') - [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)] - """ - - return cls.from_dict(json_loads(string, *args, **kwargs)) - - def yaml(self, file: File, *args: Any, **kwargs: Any) -> None: - r""" - 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: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.yaml("tests/test.yaml") - """ - - with self.open(file, mode="w") as fp: # pylint: disable=C0103 - self.yamls(fp, *args, **kwargs) - - @classmethod - def from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> Self: - r""" - 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: - (FlatDict): - - Examples: - >>> FlatDict.from_yaml('tests/test.yaml').dict() - {'a': 1, 'b': 2, 'c': 3} - """ - - kwargs.setdefault("Loader", YamlLoader) - with cls.open(file) as fp: # pylint: disable=C0103 - if isinstance(file, (IOBase, IO)): - return cls.from_yamls(fp.getvalue(), *args, **kwargs) # type: ignore[union-attr] - return cls.from_dict(yaml_load(fp, *args, **kwargs)) - - def yamls(self, *args: Any, **kwargs: Any) -> str: - r""" - Dump `FlatDict` to yaml string. - - Returns: - (str): - - Examples: - >>> FlatDict(a=1, b=2, c=3).yamls() - 'a: 1\nb: 2\nc: 3\n' - """ - - kwargs.setdefault("Dumper", YamlDumper) - kwargs.setdefault("indent", self.getattr("indent", 2)) - return yaml_dump(self.dict(), *args, **kwargs) - - @classmethod - def from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> Self: - r""" - Construct `FlatDict` from yaml string. - - Returns: - (FlatDict): - - Examples: - >>> FlatDict.from_yamls('a: 1\nb: 2\nc: 3\n').dict() - {'a': 1, 'b': 2, 'c': 3} - >>> FlatDict.from_yamls('- - a\n - 1\n- - b\n - 2\n- - c\n - 3\n').dict() - {'a': 1, 'b': 2, 'c': 3} - >>> FlatDict.from_yamls('- a: 1\n- b: 2\n- c: 3\n') - [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)] - """ - - kwargs.setdefault("Loader", SafeLoader) - return cls.from_dict(yaml_load(string, *args, **kwargs)) - - @staticmethod - @contextmanager - def open(file: File, *args: Any, encoding: str = "utf-8", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]: - r""" - 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. - - Args: - file: File path or IO. - *args: Additional arguments passed to `open`. - Defaults to (). - **kwargs: Any - Additional keyword arguments passed to `open`. - Defaults to {}. - - Yields: - (Generator[IOBase | IO, Any, Any]): - - Examples: - >>> with FlatDict.open("tests/test.yaml") as fp: - ... print(fp.read()) - a: 1 - b: 2 - c: 3 - <BLANKLINE> - >>> io = open("tests/test.yaml") - >>> with FlatDict.open(io) as fp: - ... print(fp.read()) - a: 1 - b: 2 - c: 3 - <BLANKLINE> - >>> with FlatDict.open(123, mode="w") as fp: - ... print(fp.read()) - Traceback (most recent call last): - TypeError: expected str, bytes, os.PathLike, IO or IOBase, not int - """ - - if isinstance(file, (IOBase, IO)): - yield file - elif isinstance(file, (PathLike, str, bytes)): - try: - file = open(file, *args, encoding=encoding, **kwargs) # type: ignore[call-overload] # noqa: SIM115 - yield file # type: ignore[misc] - finally: - with suppress(Exception): - file.close() # type: ignore[union-attr] - else: - raise TypeError(f"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}") - - @classmethod - def empty(cls, *args: Any, **kwargs: Any) -> Self: - r""" - 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: - (FlatDict): - - See Also: - [`empty_like`][chanfig.FlatDict.empty_like] - - Examples: - >>> d = FlatDict(a=[]) - >>> c = d.empty() - >>> c.dict() - {} - """ - - empty = cls.__new__(cls) - empty.merge(*args, **kwargs) # pylint: disable=W0212 - return empty - - def empty_like(self, *args: Any, **kwargs: Any) -> Self: - r""" - Initialise an empty copy of `FlatDict`. - - This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`. - - For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this - method. - - Returns: - (FlatDict): - - See Also: - [`empty`][chanfig.FlatDict.empty] - - Examples: - >>> d = FlatDict(a=[]) - >>> d.setattr("name", "Chang") - >>> c = d.empty_like() - >>> c.dict() - {} - >>> c.getattr("name") - 'Chang' - """ - - empty = self.empty(*args, **kwargs) - empty.__dict__.update(self.__dict__) - return empty - - def all_keys(self) -> Generator: - r""" - Equivalent to `keys`. - - This method is provided solely to make methods work on both `FlatDict` and `NestedDict`. - - See Also: - [`all_keys`][chanfig.NestedDict.all_keys] - """ - yield from self.keys() - - def all_values(self) -> Generator: - r""" - Equivalent to `keys`. - - This method is provided solely to make methods work on both `FlatDict` and `NestedDict`. - - See Also: - [`all_values`][chanfig.NestedDict.all_values] - """ - yield from self.values() - - def all_items(self) -> Generator: - r""" - Equivalent to `keys`. - - This method is provided solely to make methods work on both `FlatDict` and `NestedDict`. - - See Also: - [`all_items`][chanfig.NestedDict.all_items] - """ - yield from self.items() - - def dropnull(self) -> Self: - r""" - Drop key-value pairs with `Null` value. - - Returns: - (FlatDict): - - **Alias**: - - + `dropna` - - Examples: - >>> d = FlatDict(a=Null, b=Null, c=3) - >>> d.dict() - {'a': Null, 'b': Null, 'c': 3} - >>> d.dropnull().dict() - {'c': 3} - >>> d.dropna().dict() # alias - {'c': 3} - """ - - return self.empty({k: v for k, v in self.all_items() if v is not Null}) - - def dropna(self) -> Self: - r""" - Alias of [`dropnull`][chanfig.FlatDict.dropnull]. - """ - return self.dropnull() - - @staticmethod - def extra_repr() -> str: # pylint: disable=C0116 - return "" - - def __repr__(self) -> str: - extra_lines = [] - extra_repr = self.extra_repr() - # empty string will be split into list [''] - if extra_repr: - extra_lines = extra_repr.split("\n") - child_lines = [] - for key, value in self.items(): - key_repr = repr(key) - value_repr = repr(value) - value_repr = self._add_indent(value_repr) - child_lines.append(f"({key_repr}): {value_repr}") - # child_lines.append(f"{key_repr}: {value_repr}") - lines = extra_lines + child_lines - - main_repr = self.__class__.__name__ + "(" - if lines: - # simple one-liner info, which most builtin Modules will use - if len(extra_lines) == 1 and not child_lines: - main_repr += extra_lines[0] - elif len(child_lines) == 1 and not extra_lines and len(child_lines[0]) < 10: - main_repr += child_lines[0] - else: - main_repr += "\n " + "\n ".join(lines) + "\n" - - main_repr += ")" - return main_repr - - def _add_indent(self, text: str) -> str: - lines = text.split("\n") - # don't do anything for single-line stuff - if len(lines) == 1: - return text - first = lines.pop(0) - lines = [(self.getattr("indent", 2) * " ") + line for line in lines] - text = "\n".join(lines) - text = first + "\n" + text - return text - - def __format__(self, format_spec: str) -> str: - return repr(self.empty({k: v.__format__(format_spec) for k, v in self.all_items()})) - - def __hash__(self): - return hash(frozenset(self.items())) - - def _ipython_display_(self): # pragma: no cover - return repr(self) - - def _ipython_canary_method_should_not_exist_(self): # pragma: no cover - return None - - def aihwerij235234ljsdnp34ksodfipwoe234234jlskjdf(self): # pragma: no cover - return None - - def __rich__(self): # pragma: no cover - return self.__repr__() + for cls in self.__class__.__mro__: + if name in cls.__dict__: + return cls.__dict__[name] + return super().getattr(name, default) # type: ignore[misc] + except AttributeError: + if default is not Null: + return default + raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") from None + + def setattr(self, name: str, value: Any) -> None: + r""" + Set attribute of `FlatDict`. + + Note that it won't alter values in `FlatDict`. + + Args: + name: + value: + + Warns: + RuntimeWarning: If name already exists in `FlatDict`. + + Examples: + >>> d = FlatDict() + >>> d.setattr('attr', 'value') + >>> d.getattr('attr') + 'value' + >>> d.set('d', 1013) + >>> d.setattr('d', 1031) # RuntimeWarning: d already exists in FlatDict. + >>> d.get('d') + 1013 + >>> d.d + 1013 + >>> d.getattr('d') + 1031 + """ + + if name in self: + warn( + f"{name} already exists in {self.__class__.__name__}.\n" + f"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.", + RuntimeWarning, + ) + self.__dict__[name] = value + + def delattr(self, name: str) -> None: + r""" + Delete attribute of `FlatDict`. + + Note that it won't delete values in `FlatDict`. + + Args: + name: + + Examples: + >>> d = FlatDict() + >>> d.setattr('name', 'chang') + >>> d.getattr('name') + 'chang' + >>> d.delattr('name') + >>> d.getattr('name') + Traceback (most recent call last): + AttributeError: 'FlatDict' object has no attribute 'name' + """ + + del self.__dict__[name] + + def hasattr(self, name: str) -> bool: + r""" + Determine if an attribute exists in `FlatDict`. + + Args: + name: + + Returns: + (bool): + + Examples: + >>> d = FlatDict() + >>> d.setattr('name', 'chang') + >>> d.hasattr('name') + True + >>> d.delattr('name') + >>> d.hasattr('name') + False + """ + + try: + if name in self.__dict__ or name in self.__class__.__dict__: + return True + return super().hasattr(name) # type: ignore[misc] + except AttributeError: + return False + + def dict(self, flatten: bool = False) -> Mapping | Sequence | Set: + r""" + Convert `FlatDict` to other `Mapping`. + + Args: + flatten: Whether to flatten [`NestedDict`][chanfig.NestedDict]. + + Returns: + (Mapping): + + See Also: + [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`. + + **Alias**: + + + `to_dict` + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.dict() + {'a': 1, 'b': 2, 'c': 3} + """ + + return to_dict(self, flatten) + + def to_dict(self, flatten: bool = False) -> Mapping | Sequence | Set: + r""" + Alias of [`dict`][chanfig.FlatDict.dict]. + """ + + return self.dict(flatten) + + @classmethod + def from_dict(cls, obj: Mapping | Sequence) -> Any: # pylint: disable=R0911 + r""" + Convert `Mapping` or `Sequence` to `FlatDict`. + + Examples: + >>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3}) + FlatDict( + ('a'): 1 + ('b'): 2 + ('c'): 3 + ) + >>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)]) + FlatDict( + ('a'): 1 + ('b'): 2 + ('c'): 3 + ) + >>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}]) + [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)] + >>> FlatDict.from_dict({1, 2, 3}) + Traceback (most recent call last): + TypeError: Expected Mapping or Sequence, but got <class 'set'>. + """ + + if obj is None: + return cls() + if issubclass(cls, FlatDict): + cls = cls.empty # type: ignore[assignment] # pylint: disable=W0642 + if isinstance(obj, Mapping): + return cls(obj) + if isinstance(obj, Sequence): + try: + return cls(obj) + except ValueError: + return [cls(json) for json in obj] + raise TypeError(f"Expected Mapping or Sequence, but got {type(obj)}.") + + def sort(self, key: Callable | None = None, reverse: bool = False) -> Self: + r""" + Sort `FlatDict`. + + Returns: + (FlatDict): + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.sort().dict() + {'a': 1, 'b': 2, 'c': 3} + >>> d = FlatDict(b=2, c=3, a=1) + >>> d.sort().dict() + {'a': 1, 'b': 2, 'c': 3} + >>> a = [1] + >>> d = FlatDict(z=0, a=a) + >>> a.append(2) + >>> d.sort().dict() + {'a': [1, 2], 'z': 0} + """ + + items = sorted(self.items(), key=key, reverse=reverse) + self.clear() + for k, v in items: # pylint: disable=C0103 + self[k] = v + return self + + def interpolate( # pylint: disable=R0912 + self, use_variable: bool = True, interpolators: MutableMapping | None = None, unsafe_eval: bool = False + ) -> Self: + r""" + Perform Variable interpolation. + + Variable interpolation allows you to set the value of one key to be the value of another key easily. + + Args: + use_variable: Whether to convert values to `Variable` objects. + interpolators: Mapping contains values for interpolation. Defaults to `self`. + unsafe_eval: Whether to evaluate interpolated values. + + Raises: + 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: + >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}") + >>> d.dict() + {'a': 1, 'b': '${a}', 'c': '${a}.${b}'} + >>> d.interpolate(unsafe_eval=True).dict() + {'a': 1, 'b': 1, 'c': 1.1} + >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}") + >>> d.dict() + {'a': 1, 'b': '${a}', 'c': '${a}.${b}'} + >>> d.interpolate().dict() + {'a': 1, 'b': 1, 'c': '1.1'} + >>> isinstance(d.a, Variable) + True + >>> d.a += 1 + >>> d.dict() + {'a': 2, 'b': 2, 'c': '1.1'} + >>> d.a is d.b + True + >>> d.b is d.c + False + >>> d = FlatDict(a=1, b="${a}", c="${b}") + >>> d.dict() + {'a': 1, 'b': '${a}', 'c': '${b}'} + >>> d.interpolate(False).dict() + {'a': 1, 'b': 1, 'c': 1} + >>> isinstance(d.a, Variable) + False + >>> d.a += 1 + >>> d.dict() + {'a': 2, 'b': 1, 'c': 1} + >>> d = FlatDict(a=1, b="${b}", c="${b}") + >>> d.interpolate().dict() + Traceback (most recent call last): + ValueError: Cannot interpolate b to itself. + >>> d = FlatDict(a="${b}", b="${c}", c="${d}", d="${a}") + >>> d.interpolate().dict() + Traceback (most recent call last): + ValueError: Circular reference found: a->b->c->d->a. + >>> d = FlatDict(a=1, b="${a}", c="${d}") + >>> d.interpolate().dict() + Traceback (most recent call last): + ValueError: d is not found in FlatDict( + ('a'): '1' + ('b'): '${a}' + ('c'): '${d}' + ). + """ + # pylint: disable=C0103 + + interpolators = interpolators or self + placeholders: dict[str, list[str]] = {} + for key, value in self.all_items(): + if isinstance(value, list): + for v in value: + self.find_placeholders(key, v, placeholders) + elif isinstance(value, Mapping): + for v in value.values(): + self.find_placeholders(key, v, placeholders) + else: + self.find_placeholders(key, value, placeholders) + circular_references = find_circular_reference(placeholders) + if circular_references: + raise ValueError(f"Circular reference found: {'->'.join(circular_references)}.") + if use_variable: + placeholder_names = {i for j in placeholders.values() for i in j} + for name in list(placeholder_names.difference(placeholders.keys())): + if name not in interpolators: + raise ValueError(f"{name} is not found in {interpolators}.") + if not isinstance(interpolators[name], Variable): + interpolators[name] = Variable(interpolators[name]) + for key, value in placeholders.items(): + if isinstance(self[key], list): + for index, v in enumerate(self[key]): + self[key][index] = self.substitute(v, interpolators, value) + elif isinstance(self[key], Mapping): + for k, v in self[key].items(): + self[key][k] = self.substitute(v, interpolators, value) + else: + self[key] = self.substitute(self[key], interpolators, value) + if unsafe_eval and isinstance(self[key], str): + with suppress(SyntaxError): + self[key] = eval(self[key]) # pylint: disable=W0123 + return self + + @staticmethod + def find_placeholders(key, value, placeholders): + placeholder = find_placeholders(value) + if placeholder: + for index, name in enumerate(placeholder): + if name.startswith("."): + placeholder[index] = key.rsplit(".", 1)[0] + name + if key == name: + raise ValueError(f"Cannot interpolate {key} to itself.") + placeholders[key] = placeholder + + @staticmethod + def substitute(placeholder, interpolators, value): + try: + if len(value) == 1 and placeholder.startswith("${") and placeholder.endswith("}"): + return interpolators[value[0]] + return placeholder.replace("$", "").format(**interpolators) + except KeyError as exc: + raise ValueError(f"{exc} is not found in {interpolators}.") from None + + def merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> Self: + r""" + Merge `other` into `FlatDict`. + + Args: + *args: `Mapping` or `Sequence` to be merged. + overwrite: Whether to overwrite existing values. + **kwargs: `Mapping` to be merged. + + Returns: + self: + + **Alias**: + + + `union` + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> n = {'b': 'b', 'c': 'c', 'd': 'd'} + >>> d.merge(n).dict() + {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'} + >>> l = [('c', 3), ('d', 4)] + >>> d.merge(l).dict() + {'a': 1, 'b': 'b', 'c': 3, 'd': 4} + >>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict() # alias + {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'} + >>> d = FlatDict() + >>> d.merge({1: 1, 2: 2, 3:3}).dict() + {1: 1, 2: 2, 3: 3} + >>> d.merge(d.clone()).dict() + {1: 1, 2: 2, 3: 3} + >>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict() + {1: 1, 2: 2, 3: 3, 4: 4, 5: 5} + """ + + if len(args) == 1: + args = args[0] + if isinstance(args, (PathLike, str, bytes)): + args = self.load(args) # type: ignore[assignment] + warn( + "merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.", + PendingDeprecationWarning, + ) + self._merge(self, args, overwrite=overwrite) + elif len(args) > 1: + self._merge(self, args, overwrite=overwrite) + if kwargs: + self._merge(self, kwargs, overwrite=overwrite) + return self + + @staticmethod + def _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping: + if not that: + return this + if isinstance(that, Mapping): + that = that.items() + for key, value in that: + if key in this and isinstance(this[key], Mapping): + if isinstance(value, Mapping): + FlatDict._merge(this[key], value) + elif overwrite: + if isinstance(value, FlatDict): + this.set(key, value) + else: + this[key] = value + elif overwrite or key not in this: + this.set(key, value) + return this + + def union(self, *args: Any, **kwargs: Any) -> Self: + r""" + Alias of [`merge`][chanfig.FlatDict.merge]. + """ + return self.merge(*args, **kwargs) + + def merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> Self: + r""" + Merge content of `file` into `FlatDict`. + + Args: + file (File): + *args: Passed to [`load`][chanfig.FlatDict.load]. + **kwargs: Passed to [`load`][chanfig.FlatDict.load]. + + Returns: + self: + + Examples: + >>> d = FlatDict(a=1, b=1) + >>> d.merge_from_file("tests/test.yaml").dict() + {'a': 1, 'b': 2, 'c': 3} + """ + + return self.merge(self.load(file, *args, **kwargs)) + + def intersect(self, other: Mapping | Iterable | PathStr) -> Self: + r""" + Intersection of `FlatDict` and `other`. + + Args: + other (Mapping | Iterable | PathStr): + + Returns: + (FlatDict): + + **Alias**: + + + `inter` + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> n = {'b': 'b', 'c': 'c', 'd': 'd'} + >>> d.intersect(n).dict() + {} + >>> l = [('c', 3), ('d', 4)] + >>> d.intersect(l).dict() + {'c': 3} + >>> d.merge(l).intersect("tests/test.yaml").dict() + {'a': 1, 'b': 2, 'c': 3} + >>> d.intersect(1) + Traceback (most recent call last): + TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>. + >>> d.inter(FlatDict(b='b', c='c', d='d')).dict() # alias + {} + """ + + if isinstance(other, (PathLike, str, bytes)): + other = self.load(other) + if isinstance(other, (Mapping,)): + other = self.empty(other).items() + if not isinstance(other, Iterable): + raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.") + return self.empty(**{key: value for key, value in other if key in self and self[key] == value}) # type: ignore + + def inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self: + r""" + Alias of [`intersect`][chanfig.FlatDict.intersect]. + """ + return self.intersect(other, *args, **kwargs) + + def difference(self, other: Mapping | Iterable | PathStr) -> Self: + r""" + Difference between `FlatDict` and `other`. + + Args: + other: + + Returns: + (FlatDict): + + **Alias**: + + + `diff` + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> n = {'b': 'b', 'c': 'c', 'd': 'd'} + >>> d.difference(n).dict() + {'b': 'b', 'c': 'c', 'd': 'd'} + >>> l = [('c', 3), ('d', 4)] + >>> d.difference(l).dict() + {'d': 4} + >>> d.merge(l).difference("tests/test.yaml").dict() + {} + >>> d.difference(1) + Traceback (most recent call last): + TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>. + >>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict() # alias + {'b': 'b', 'c': 'c', 'd': 'd'} + """ + + if isinstance(other, (PathLike, str, bytes)): + other = self.load(other) + if isinstance(other, (Mapping,)): + other = self.empty(other).items() + if not isinstance(other, Iterable): + raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.") + return self.empty( + **{key: value for key, value in other if key not in self or self[key] != value} # type: ignore[misc] + ) + + def diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self: + r""" + Alias of [`difference`][chanfig.FlatDict.difference]. + """ + return self.difference(other, *args, **kwargs) + + def to(self, cls: str | TorchDevice | TorchDType) -> Self: # pragma: no cover + r""" + Convert values of `FlatDict` to target `cls`. + + Args: + cls (str | torch.device | torch.dtype): + + Returns: + self: + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.to(int) + Traceback (most recent call last): + TypeError: to() only support torch.dtype and torch.device, but got <class 'int'>. + """ + + # pylint: disable=C0103 + + if isinstance(cls, (str, TorchDevice, TorchDType)): + for k, v in self.all_items(): + if hasattr(v, "to"): + self[k] = v.to(cls) + return self + + raise TypeError(f"to() only support torch.dtype and torch.device, but got {cls}.") + + def cpu(self) -> Self: # pragma: no cover + r""" + Move all tensors to cpu. + + Returns: + self: + + Examples: + >>> import torch + >>> d = FlatDict(a=torch.tensor(1)) + >>> d.cpu().dict() # doctest: +SKIP + {'a': tensor(1, device='cpu')} + """ + + return self.to(TorchDevice("cpu")) + + def gpu(self) -> Self: # pragma: no cover + r""" + Move all tensors to gpu. + + Returns: + self: + + **Alias**: + + + `cuda` + + Examples: + >>> import torch + >>> d = FlatDict(a=torch.tensor(1)) + >>> d.gpu().dict() # doctest: +SKIP + {'a': tensor(1, device='cuda:0')} + >>> d.cuda().dict() # alias # doctest: +SKIP + {'a': tensor(1, device='cuda:0')} + """ + + return self.to(TorchDevice("cuda")) + + def cuda(self) -> Self: # pragma: no cover + r""" + Alias of [`gpu`][chanfig.FlatDict.gpu]. + """ + return self.gpu() + + def tpu(self) -> Self: # pragma: no cover + r""" + Move all tensors to tpu. + + Returns: + self: + + **Alias**: + + + `xla` + + Examples: + >>> import torch + >>> d = FlatDict(a=torch.tensor(1)) + >>> d.tpu().dict() # doctest: +SKIP + {'a': tensor(1, device='xla:0')} + >>> d.xla().dict() # alias # doctest: +SKIP + {'a': tensor(1, device='xla:0')} + """ + + return self.to(TorchDevice("xla")) + + def xla(self) -> Self: # pragma: no cover + r""" + Alias of [`tpu`][chanfig.FlatDict.tpu]. + """ + return self.tpu() + + def copy(self) -> Self: + r""" + Create a shallow copy of `FlatDict`. + + Returns: + (FlatDict): + + Examples: + >>> d = FlatDict(a=[]) + >>> d.setattr("name", "Chang") + >>> c = d.copy() + >>> c.dict() + {'a': []} + >>> d.a.append(1) + >>> c.dict() + {'a': [1]} + >>> c.getattr("name") + 'Chang' + """ + + return copy(self) + + def __deepcopy__(self, memo: Mapping | None = None) -> Self: + # pylint: disable=C0103 + + if memo is not None and id(self) in memo: + return memo[id(self)] + ret = self.empty() + ret.__dict__.update(deepcopy(self.__dict__)) + for k, v in self.items(): + if isinstance(v, FlatDict): + ret[k] = v.deepcopy(memo=memo) + else: + ret[k] = deepcopy(v) + return ret + + def deepcopy(self, memo: Mapping | None = None) -> Self: # pylint: disable=W0613 + r""" + Create a deep copy of `FlatDict`. + + Returns: + (FlatDict): + + **Alias**: + + + `clone` + + Examples: + >>> d = FlatDict(a=[]) + >>> d.setattr("name", "Chang") + >>> c = d.deepcopy() + >>> c.dict() + {'a': []} + >>> d.a.append(1) + >>> c.dict() + {'a': []} + >>> c.getattr("name") + 'Chang' + >>> d == d.clone() # alias + True + """ + + return deepcopy(self) + + def clone(self, memo: Mapping | None = None) -> Self: + r""" + Alias of [`deepcopy`][chanfig.FlatDict.deepcopy]. + """ + return self.deepcopy(memo=memo) + + def save( # pylint: disable=W1113 + self, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment] + ) -> None: + r""" + Save `FlatDict` to file. + + Raises: + ValueError: If save to `IO` and `method` is not specified. + TypeError: If save to unsupported extension. + + **Alias**: + + + `save` + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.save("tests/test.yaml") + >>> d.save("test.conf") + Traceback (most recent call last): + TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf. + >>> with open("test.yaml", "w") as f: + ... d.save(f) + Traceback (most recent call last): + ValueError: `method` must be specified when saving to IO. + """ + + if method is None: + if isinstance(file, (IOBase, IO)): + raise ValueError("`method` must be specified when saving to IO.") + method = splitext(file)[-1][1:] + extension = method.lower() + if extension in YAML: + return self.yaml(file=file, *args, **kwargs) # type: ignore[misc] # noqa: B026 + if extension in JSON: + return self.json(file=file, *args, **kwargs) # type: ignore[misc] # noqa: B026 + raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.") + + def dump( # pylint: disable=W1113 + self, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment] + ) -> None: + r""" + Alias of [`save`][chanfig.FlatDict.save]. + """ + return self.save(file, method, *args, **kwargs) + + @classmethod + def load( # pylint: disable=W1113 + cls, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment] + ) -> Self: + """ + Load `FlatDict` from file. + + Args: + file: File to load from. + method: File type, should be in `JSON` or `YAML`. + + Returns: + (FlatDict): + + Raises: + ValueError: If load from `IO` and `method` is not specified. + TypeError: If dump to unsupported extension. + + Examples: + >>> d = FlatDict.load("tests/test.yaml") + >>> d.dict() + {'a': 1, 'b': 2, 'c': 3} + >>> d.load("tests/test.conf") + Traceback (most recent call last): + TypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf. + >>> with open("tests/test.yaml") as f: + ... d.load(f) + Traceback (most recent call last): + ValueError: `method` must be specified when loading from IO. + """ + + if method is None: + if isinstance(file, (IOBase, IO)): + raise ValueError("`method` must be specified when loading from IO.") + method = splitext(file)[-1][1:] + extension = method.lower() + if extension in JSON: + return cls.from_json(file, *args, **kwargs) + if extension in YAML: + return cls.from_yaml(file, *args, **kwargs) + raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.") + + def json(self, file: File, *args: Any, **kwargs: Any) -> None: + r""" + 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: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.json("tests/test.json") + """ + + with self.open(file, mode="w") as fp: # pylint: disable=C0103 + fp.write(self.jsons(*args, **kwargs)) + + @classmethod + def from_json(cls, file: File, *args: Any, **kwargs: Any) -> Self: + r""" + 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: + (FlatDict): + + Examples: + >>> d = FlatDict.from_json('tests/test.json') + >>> d.dict() + {'a': 1, 'b': 2, 'c': 3} + """ + + with cls.open(file) as fp: # pylint: disable=C0103 + if isinstance(file, (IOBase, IO)): + return cls.from_jsons(fp.getvalue(), *args, **kwargs) # type: ignore[union-attr] + return cls.from_jsons(fp.read(), *args, **kwargs) + + def jsons(self, *args: Any, **kwargs: Any) -> str: + r""" + Dump `FlatDict` to json string. + + Returns: + (str): + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.jsons() + '{\n "a": 1,\n "b": 2,\n "c": 3\n}' + """ + + kwargs.setdefault("cls", JsonEncoder) + kwargs.setdefault("indent", self.getattr("indent", 2)) + return json_dumps(self.dict(), *args, **kwargs) + + @classmethod + def from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> Self: + r""" + Construct `FlatDict` from json string. + + Returns: + (FlatDict): + + Examples: + >>> FlatDict.from_jsons('{\n "a": 1,\n "b": 2,\n "c": 3\n}').dict() + {'a': 1, 'b': 2, 'c': 3} + >>> FlatDict.from_jsons('[["a", 1], ["b", 2], ["c", 3]]').dict() + {'a': 1, 'b': 2, 'c': 3} + >>> FlatDict.from_jsons('[{"a": 1}, {"b": 2}, {"c": 3}]') + [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)] + """ + + return cls.from_dict(json_loads(string, *args, **kwargs)) + + def yaml(self, file: File, *args: Any, **kwargs: Any) -> None: + r""" + 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: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.yaml("tests/test.yaml") + """ + + with self.open(file, mode="w") as fp: # pylint: disable=C0103 + self.yamls(fp, *args, **kwargs) + + @classmethod + def from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> Self: + r""" + 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: + (FlatDict): + + Examples: + >>> FlatDict.from_yaml('tests/test.yaml').dict() + {'a': 1, 'b': 2, 'c': 3} + """ + + kwargs.setdefault("Loader", YamlLoader) + with cls.open(file) as fp: # pylint: disable=C0103 + if isinstance(file, (IOBase, IO)): + return cls.from_yamls(fp.getvalue(), *args, **kwargs) # type: ignore[union-attr] + return cls.from_dict(yaml_load(fp, *args, **kwargs)) + + def yamls(self, *args: Any, **kwargs: Any) -> str: + r""" + Dump `FlatDict` to yaml string. + + Returns: + (str): + + Examples: + >>> FlatDict(a=1, b=2, c=3).yamls() + 'a: 1\nb: 2\nc: 3\n' + """ + + kwargs.setdefault("Dumper", YamlDumper) + kwargs.setdefault("indent", self.getattr("indent", 2)) + return yaml_dump(self.dict(), *args, **kwargs) + + @classmethod + def from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> Self: + r""" + Construct `FlatDict` from yaml string. + + Returns: + (FlatDict): + + Examples: + >>> FlatDict.from_yamls('a: 1\nb: 2\nc: 3\n').dict() + {'a': 1, 'b': 2, 'c': 3} + >>> FlatDict.from_yamls('- - a\n - 1\n- - b\n - 2\n- - c\n - 3\n').dict() + {'a': 1, 'b': 2, 'c': 3} + >>> FlatDict.from_yamls('- a: 1\n- b: 2\n- c: 3\n') + [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)] + """ + + kwargs.setdefault("Loader", SafeLoader) + return cls.from_dict(yaml_load(string, *args, **kwargs)) + + @staticmethod + @contextmanager + def open(file: File, *args: Any, encoding: str = "utf-8", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]: + r""" + 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. + + Args: + file: File path or IO. + *args: Additional arguments passed to `open`. + Defaults to (). + **kwargs: Any + Additional keyword arguments passed to `open`. + Defaults to {}. + + Yields: + (Generator[IOBase | IO, Any, Any]): + + Examples: + >>> with FlatDict.open("tests/test.yaml") as fp: + ... print(fp.read()) + a: 1 + b: 2 + c: 3 + <BLANKLINE> + >>> io = open("tests/test.yaml") + >>> with FlatDict.open(io) as fp: + ... print(fp.read()) + a: 1 + b: 2 + c: 3 + <BLANKLINE> + >>> with FlatDict.open(123, mode="w") as fp: + ... print(fp.read()) + Traceback (most recent call last): + TypeError: expected str, bytes, os.PathLike, IO or IOBase, not int + """ + + if isinstance(file, (IOBase, IO)): + yield file + elif isinstance(file, (PathLike, str, bytes)): + try: + file = open(file, *args, encoding=encoding, **kwargs) # type: ignore[call-overload] # noqa: SIM115 + yield file # type: ignore[misc] + finally: + with suppress(Exception): + file.close() # type: ignore[union-attr] + else: + raise TypeError(f"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}") + + @classmethod + def empty(cls, *args: Any, **kwargs: Any) -> Self: + r""" + 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: + (FlatDict): + + See Also: + [`empty_like`][chanfig.FlatDict.empty_like] + + Examples: + >>> d = FlatDict(a=[]) + >>> c = d.empty() + >>> c.dict() + {} + """ + + empty = cls.__new__(cls) + empty.merge(*args, **kwargs) # pylint: disable=W0212 + return empty + + def empty_like(self, *args: Any, **kwargs: Any) -> Self: + r""" + Initialise an empty copy of `FlatDict`. + + This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`. + + For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this + method. + + Returns: + (FlatDict): + + See Also: + [`empty`][chanfig.FlatDict.empty] + + Examples: + >>> d = FlatDict(a=[]) + >>> d.setattr("name", "Chang") + >>> c = d.empty_like() + >>> c.dict() + {} + >>> c.getattr("name") + 'Chang' + """ + + empty = self.empty(*args, **kwargs) + empty.__dict__.update(self.__dict__) + return empty + + def all_keys(self) -> Generator: + r""" + Equivalent to `keys`. + + This method is provided solely to make methods work on both `FlatDict` and `NestedDict`. + + See Also: + [`all_keys`][chanfig.NestedDict.all_keys] + """ + yield from self.keys() + + def all_values(self) -> Generator: + r""" + Equivalent to `keys`. + + This method is provided solely to make methods work on both `FlatDict` and `NestedDict`. + + See Also: + [`all_values`][chanfig.NestedDict.all_values] + """ + yield from self.values() + + def all_items(self) -> Generator: + r""" + Equivalent to `keys`. + + This method is provided solely to make methods work on both `FlatDict` and `NestedDict`. + + See Also: + [`all_items`][chanfig.NestedDict.all_items] + """ + yield from self.items() + + def dropnull(self) -> Self: + r""" + Drop key-value pairs with `Null` value. + + Returns: + (FlatDict): + + **Alias**: + + + `dropna` + + Examples: + >>> d = FlatDict(a=Null, b=Null, c=3) + >>> d.dict() + {'a': Null, 'b': Null, 'c': 3} + >>> d.dropnull().dict() + {'c': 3} + >>> d.dropna().dict() # alias + {'c': 3} + """ + + return self.empty({k: v for k, v in self.all_items() if v is not Null}) + + def dropna(self) -> Self: + r""" + Alias of [`dropnull`][chanfig.FlatDict.dropnull]. + """ + return self.dropnull() + + @staticmethod + def extra_repr() -> str: # pylint: disable=C0116 + return "" + + def __repr__(self) -> str: + extra_lines = [] + extra_repr = self.extra_repr() + # empty string will be split into list [''] + if extra_repr: + extra_lines = extra_repr.split("\n") + child_lines = [] + for key, value in self.items(): + key_repr = repr(key) + value_repr = repr(value) + value_repr = self._add_indent(value_repr) + child_lines.append(f"({key_repr}): {value_repr}") + # child_lines.append(f"{key_repr}: {value_repr}") + lines = extra_lines + child_lines + + main_repr = self.__class__.__name__ + "(" + if lines: + # simple one-liner info, which most builtin Modules will use + if len(extra_lines) == 1 and not child_lines: + main_repr += extra_lines[0] + elif len(child_lines) == 1 and not extra_lines and len(child_lines[0]) < 10: + main_repr += child_lines[0] + else: + main_repr += "\n " + "\n ".join(lines) + "\n" + + main_repr += ")" + return main_repr + + def _add_indent(self, text: str) -> str: + lines = text.split("\n") + # don't do anything for single-line stuff + if len(lines) == 1: + return text + first = lines.pop(0) + lines = [(self.getattr("indent", 2) * " ") + line for line in lines] + text = "\n".join(lines) + text = first + "\n" + text + return text + + def __format__(self, format_spec: str) -> str: + return repr(self.empty({k: v.__format__(format_spec) for k, v in self.all_items()})) + + def __hash__(self): + return hash(frozenset(self.items())) + + def _ipython_display_(self): # pragma: no cover + return repr(self) + + def _ipython_canary_method_should_not_exist_(self): # pragma: no cover + return None + + def aihwerij235234ljsdnp34ksodfipwoe234234jlskjdf(self): # pragma: no cover + return None + + def __rich__(self): # pragma: no cover + return self.__repr__()

@@ -4354,8 +4356,7 @@

Source code in chanfig/flat_dict.py -
Python
1433
-1434
+              
Python
1434
 1435
 1436
 1437
@@ -4363,16 +4364,17 @@ 

1439 1440 1441 -1442

def all_items(self) -> Generator:
-    r"""
-    Equivalent to `keys`.
-
-    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.
-
-    See Also:
-        [`all_items`][chanfig.NestedDict.all_items]
-    """
-    yield from self.items()
+1442
+1443
def all_items(self) -> Generator:
+    r"""
+    Equivalent to `keys`.
+
+    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.
+
+    See Also:
+        [`all_items`][chanfig.NestedDict.all_items]
+    """
+    yield from self.items()
 
@@ -4400,8 +4402,7 @@

Source code in chanfig/flat_dict.py -
Python
1411
-1412
+              
Python
1412
 1413
 1414
 1415
@@ -4409,16 +4410,17 @@ 

1417 1418 1419 -1420

def all_keys(self) -> Generator:
-    r"""
-    Equivalent to `keys`.
-
-    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.
-
-    See Also:
-        [`all_keys`][chanfig.NestedDict.all_keys]
-    """
-    yield from self.keys()
+1420
+1421
def all_keys(self) -> Generator:
+    r"""
+    Equivalent to `keys`.
+
+    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.
+
+    See Also:
+        [`all_keys`][chanfig.NestedDict.all_keys]
+    """
+    yield from self.keys()
 
@@ -4446,8 +4448,7 @@

Source code in chanfig/flat_dict.py -
Python
1422
-1423
+              
Python
1423
 1424
 1425
 1426
@@ -4455,16 +4456,17 @@ 

1428 1429 1430 -1431

def all_values(self) -> Generator:
-    r"""
-    Equivalent to `keys`.
-
-    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.
-
-    See Also:
-        [`all_values`][chanfig.NestedDict.all_values]
-    """
-    yield from self.values()
+1431
+1432
def all_values(self) -> Generator:
+    r"""
+    Equivalent to `keys`.
+
+    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.
+
+    See Also:
+        [`all_values`][chanfig.NestedDict.all_values]
+    """
+    yield from self.values()
 
@@ -4486,15 +4488,15 @@

Source code in chanfig/flat_dict.py -
Python
1065
-1066
+              
Python
def clone(self, memo: Mapping | None = None) -> Self:
-    r"""
-    Alias of [`deepcopy`][chanfig.FlatDict.deepcopy].
-    """
-    return self.deepcopy(memo=memo)
+1069
+1070
def clone(self, memo: Mapping | None = None) -> Self:
+    r"""
+    Alias of [`deepcopy`][chanfig.FlatDict.deepcopy].
+    """
+    return self.deepcopy(memo=memo)
 
@@ -4553,8 +4555,7 @@

Source code in chanfig/flat_dict.py -
Python
1001
-1002
+              
Python
1002
 1003
 1004
 1005
@@ -4573,27 +4574,28 @@ 

1018 1019 1020 -1021

def copy(self) -> Self:
-    r"""
-    Create a shallow copy of `FlatDict`.
-
-    Returns:
-        (FlatDict):
-
-    Examples:
-        >>> d = FlatDict(a=[])
-        >>> d.setattr("name", "Chang")
-        >>> c = d.copy()
-        >>> c.dict()
-        {'a': []}
-        >>> d.a.append(1)
-        >>> c.dict()
-        {'a': [1]}
-        >>> c.getattr("name")
-        'Chang'
-    """
-
-    return copy(self)
+1021
+1022
def copy(self) -> Self:
+    r"""
+    Create a shallow copy of `FlatDict`.
+
+    Returns:
+        (FlatDict):
+
+    Examples:
+        >>> d = FlatDict(a=[])
+        >>> d.setattr("name", "Chang")
+        >>> c = d.copy()
+        >>> c.dict()
+        {'a': []}
+        >>> d.a.append(1)
+        >>> c.dict()
+        {'a': [1]}
+        >>> c.getattr("name")
+        'Chang'
+    """
+
+    return copy(self)
 
@@ -4646,8 +4648,7 @@

Source code in chanfig/flat_dict.py -
Python
929
-930
+              
Python
930
 931
 932
 933
@@ -4660,21 +4661,22 @@ 

940 941 942 -943

def cpu(self) -> Self:  # pragma: no cover
-    r"""
-    Move all tensors to cpu.
-
-    Returns:
-        self:
-
-    Examples:
-        >>> import torch
-        >>> d = FlatDict(a=torch.tensor(1))
-        >>> d.cpu().dict()  # doctest: +SKIP
-        {'a': tensor(1, device='cpu')}
-    """
-
-    return self.to(TorchDevice("cpu"))
+943
+944
def cpu(self) -> Self:  # pragma: no cover
+    r"""
+    Move all tensors to cpu.
+
+    Returns:
+        self:
+
+    Examples:
+        >>> import torch
+        >>> d = FlatDict(a=torch.tensor(1))
+        >>> d.cpu().dict()  # doctest: +SKIP
+        {'a': tensor(1, device='cpu')}
+    """
+
+    return self.to(TorchDevice("cpu"))
 
@@ -4696,15 +4698,15 @@

Source code in chanfig/flat_dict.py -
Python
967
-968
+              
Python
def cuda(self) -> Self:  # pragma: no cover
-    r"""
-    Alias of [`gpu`][chanfig.FlatDict.gpu].
-    """
-    return self.gpu()
+971
+972
def cuda(self) -> Self:  # pragma: no cover
+    r"""
+    Alias of [`gpu`][chanfig.FlatDict.gpu].
+    """
+    return self.gpu()
 
@@ -4769,8 +4771,7 @@

Source code in chanfig/flat_dict.py -
Python
1037
-1038
+              
Python
1038
 1039
 1040
 1041
@@ -4795,33 +4796,34 @@ 

1060 1061 1062 -1063

def deepcopy(self, memo: Mapping | None = None) -> Self:  # pylint: disable=W0613
-    r"""
-    Create a deep copy of `FlatDict`.
-
-    Returns:
-        (FlatDict):
-
-    **Alias**:
-
-    + `clone`
-
-    Examples:
-        >>> d = FlatDict(a=[])
-        >>> d.setattr("name", "Chang")
-        >>> c = d.deepcopy()
-        >>> c.dict()
-        {'a': []}
-        >>> d.a.append(1)
-        >>> c.dict()
-        {'a': []}
-        >>> c.getattr("name")
-        'Chang'
-        >>> d == d.clone()  # alias
-        True
-    """
-
-    return deepcopy(self)
+1063
+1064
def deepcopy(self, memo: Mapping | None = None) -> Self:  # pylint: disable=W0613
+    r"""
+    Create a deep copy of `FlatDict`.
+
+    Returns:
+        (FlatDict):
+
+    **Alias**:
+
+    + `clone`
+
+    Examples:
+        >>> d = FlatDict(a=[])
+        >>> d.setattr("name", "Chang")
+        >>> c = d.deepcopy()
+        >>> c.dict()
+        {'a': []}
+        >>> d.a.append(1)
+        >>> c.dict()
+        {'a': []}
+        >>> c.getattr("name")
+        'Chang'
+        >>> d == d.clone()  # alias
+        True
+    """
+
+    return deepcopy(self)
 
@@ -4885,8 +4887,7 @@

Source code in chanfig/flat_dict.py -
Python
444
-445
+              
Python
445
 446
 447
 448
@@ -4905,27 +4906,28 @@ 

461 462 463 -464

def delattr(self, name: str) -> None:
-    r"""
-    Delete attribute of `FlatDict`.
-
-    Note that it won't delete values in `FlatDict`.
-
-    Args:
-        name:
-
-    Examples:
-        >>> d = FlatDict()
-        >>> d.setattr('name', 'chang')
-        >>> d.getattr('name')
-        'chang'
-        >>> d.delattr('name')
-        >>> d.getattr('name')
-        Traceback (most recent call last):
-        AttributeError: 'FlatDict' object has no attribute 'name'
-    """
-
-    del self.__dict__[name]
+464
+465
def delattr(self, name: str) -> None:
+    r"""
+    Delete attribute of `FlatDict`.
+
+    Note that it won't delete values in `FlatDict`.
+
+    Args:
+        name:
+
+    Examples:
+        >>> d = FlatDict()
+        >>> d.setattr('name', 'chang')
+        >>> d.getattr('name')
+        'chang'
+        >>> d.delattr('name')
+        >>> d.getattr('name')
+        Traceback (most recent call last):
+        AttributeError: 'FlatDict' object has no attribute 'name'
+    """
+
+    del self.__dict__[name]
 
@@ -5138,8 +5140,7 @@

Source code in chanfig/flat_dict.py -
Python
493
-494
+              
Python
494
 495
 496
 497
@@ -5161,30 +5162,31 @@ 

513 514 515 -516

def dict(self, flatten: bool = False) -> Mapping | Sequence | Set:
-    r"""
-    Convert `FlatDict` to other `Mapping`.
-
-    Args:
-        flatten: Whether to flatten [`NestedDict`][chanfig.NestedDict].
-
-    Returns:
-        (Mapping):
-
-    See Also:
-        [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`.
-
-    **Alias**:
-
-    + `to_dict`
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.dict()
-        {'a': 1, 'b': 2, 'c': 3}
-    """
-
-    return to_dict(self, flatten)
+516
+517
def dict(self, flatten: bool = False) -> Mapping | Sequence | Set:
+    r"""
+    Convert `FlatDict` to other `Mapping`.
+
+    Args:
+        flatten: Whether to flatten [`NestedDict`][chanfig.NestedDict].
+
+    Returns:
+        (Mapping):
+
+    See Also:
+        [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`.
+
+    **Alias**:
+
+    + `to_dict`
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.dict()
+        {'a': 1, 'b': 2, 'c': 3}
+    """
+
+    return to_dict(self, flatten)
 
@@ -5206,15 +5208,15 @@

Source code in chanfig/flat_dict.py -
Python
896
-897
+              
Python
def diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Alias of [`difference`][chanfig.FlatDict.difference].
-    """
-    return self.difference(other, *args, **kwargs)
+900
+901
def diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Alias of [`difference`][chanfig.FlatDict.difference].
+    """
+    return self.difference(other, *args, **kwargs)
 
@@ -5310,8 +5312,7 @@

Source code in chanfig/flat_dict.py -
Python
855
-856
+              
Python
856
 857
 858
 859
@@ -5349,46 +5350,47 @@ 

891 892 893 -894

def difference(self, other: Mapping | Iterable | PathStr) -> Self:
-    r"""
-    Difference between `FlatDict` and `other`.
-
-    Args:
-        other:
-
-    Returns:
-        (FlatDict):
-
-    **Alias**:
-
-    + `diff`
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}
-        >>> d.difference(n).dict()
-        {'b': 'b', 'c': 'c', 'd': 'd'}
-        >>> l = [('c', 3), ('d', 4)]
-        >>> d.difference(l).dict()
-        {'d': 4}
-        >>> d.merge(l).difference("tests/test.yaml").dict()
-        {}
-        >>> d.difference(1)
-        Traceback (most recent call last):
-        TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.
-        >>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict()  # alias
-        {'b': 'b', 'c': 'c', 'd': 'd'}
-    """
-
-    if isinstance(other, (PathLike, str, bytes)):
-        other = self.load(other)
-    if isinstance(other, (Mapping,)):
-        other = self.empty(other).items()
-    if not isinstance(other, Iterable):
-        raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.")
-    return self.empty(
-        **{key: value for key, value in other if key not in self or self[key] != value}  # type: ignore[misc]
-    )
+894
+895
def difference(self, other: Mapping | Iterable | PathStr) -> Self:
+    r"""
+    Difference between `FlatDict` and `other`.
+
+    Args:
+        other:
+
+    Returns:
+        (FlatDict):
+
+    **Alias**:
+
+    + `diff`
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}
+        >>> d.difference(n).dict()
+        {'b': 'b', 'c': 'c', 'd': 'd'}
+        >>> l = [('c', 3), ('d', 4)]
+        >>> d.difference(l).dict()
+        {'d': 4}
+        >>> d.merge(l).difference("tests/test.yaml").dict()
+        {}
+        >>> d.difference(1)
+        Traceback (most recent call last):
+        TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.
+        >>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict()  # alias
+        {'b': 'b', 'c': 'c', 'd': 'd'}
+    """
+
+    if isinstance(other, (PathLike, str, bytes)):
+        other = self.load(other)
+    if isinstance(other, (Mapping,)):
+        other = self.empty(other).items()
+    if not isinstance(other, Iterable):
+        raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.")
+    return self.empty(
+        **{key: value for key, value in other if key not in self or self[key] != value}  # type: ignore[misc]
+    )
 
@@ -5410,15 +5412,15 @@

Source code in chanfig/flat_dict.py -
Python
1467
-1468
+              
Python
def dropna(self) -> Self:
-    r"""
-    Alias of [`dropnull`][chanfig.FlatDict.dropnull].
-    """
-    return self.dropnull()
+1471
+1472
def dropna(self) -> Self:
+    r"""
+    Alias of [`dropnull`][chanfig.FlatDict.dropnull].
+    """
+    return self.dropnull()
 
@@ -5478,8 +5480,7 @@

Source code in chanfig/flat_dict.py -
Python
1444
-1445
+              
Python
1445
 1446
 1447
 1448
@@ -5499,28 +5500,29 @@ 

1462 1463 1464 -1465

def dropnull(self) -> Self:
-    r"""
-    Drop key-value pairs with `Null` value.
-
-    Returns:
-        (FlatDict):
-
-    **Alias**:
-
-    + `dropna`
-
-    Examples:
-        >>> d = FlatDict(a=Null, b=Null, c=3)
-        >>> d.dict()
-        {'a': Null, 'b': Null, 'c': 3}
-        >>> d.dropnull().dict()
-        {'c': 3}
-        >>> d.dropna().dict()  # alias
-        {'c': 3}
-    """
-
-    return self.empty({k: v for k, v in self.all_items() if v is not Null})
+1465
+1466
def dropnull(self) -> Self:
+    r"""
+    Drop key-value pairs with `Null` value.
+
+    Returns:
+        (FlatDict):
+
+    **Alias**:
+
+    + `dropna`
+
+    Examples:
+        >>> d = FlatDict(a=Null, b=Null, c=3)
+        >>> d.dict()
+        {'a': Null, 'b': Null, 'c': 3}
+        >>> d.dropnull().dict()
+        {'c': 3}
+        >>> d.dropna().dict()  # alias
+        {'c': 3}
+    """
+
+    return self.empty({k: v for k, v in self.all_items() if v is not Null})
 
@@ -5542,19 +5544,19 @@

Source code in chanfig/flat_dict.py -
Python
1108
-1109
+              
Python
def dump(  # pylint: disable=W1113
-    self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]
-) -> None:
-    r"""
-    Alias of [`save`][chanfig.FlatDict.save].
-    """
-    return self.save(file, method, *args, **kwargs)
+1114
+1115
def dump(  # pylint: disable=W1113
+    self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]
+) -> None:
+    r"""
+    Alias of [`save`][chanfig.FlatDict.save].
+    """
+    return self.save(file, method, *args, **kwargs)
 
@@ -5619,8 +5621,7 @@

Source code in chanfig/flat_dict.py -
Python
1355
-1356
+              
Python
1356
 1357
 1358
 1359
@@ -5644,32 +5645,33 @@ 

1377 1378 1379 -1380

@classmethod
-def empty(cls, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    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:
-        (FlatDict):
-
-    See Also:
-        [`empty_like`][chanfig.FlatDict.empty_like]
-
-    Examples:
-        >>> d = FlatDict(a=[])
-        >>> c = d.empty()
-        >>> c.dict()
-        {}
-    """
-
-    empty = cls.__new__(cls)
-    empty.merge(*args, **kwargs)  # pylint: disable=W0212
-    return empty
+1380
+1381
@classmethod
+def empty(cls, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    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:
+        (FlatDict):
+
+    See Also:
+        [`empty_like`][chanfig.FlatDict.empty_like]
+
+    Examples:
+        >>> d = FlatDict(a=[])
+        >>> c = d.empty()
+        >>> c.dict()
+        {}
+    """
+
+    empty = cls.__new__(cls)
+    empty.merge(*args, **kwargs)  # pylint: disable=W0212
+    return empty
 
@@ -5733,8 +5735,7 @@

Source code in chanfig/flat_dict.py -
Python
1382
-1383
+              
Python
1383
 1384
 1385
 1386
@@ -5760,34 +5761,35 @@ 

1406 1407 1408 -1409

def empty_like(self, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Initialise an empty copy of `FlatDict`.
-
-    This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`.
-
-    For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this
-    method.
-
-    Returns:
-        (FlatDict):
-
-    See Also:
-        [`empty`][chanfig.FlatDict.empty]
-
-    Examples:
-        >>> d = FlatDict(a=[])
-        >>> d.setattr("name", "Chang")
-        >>> c = d.empty_like()
-        >>> c.dict()
-        {}
-        >>> c.getattr("name")
-        'Chang'
-    """
-
-    empty = self.empty(*args, **kwargs)
-    empty.__dict__.update(self.__dict__)
-    return empty
+1409
+1410
def empty_like(self, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Initialise an empty copy of `FlatDict`.
+
+    This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`.
+
+    For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this
+    method.
+
+    Returns:
+        (FlatDict):
+
+    See Also:
+        [`empty`][chanfig.FlatDict.empty]
+
+    Examples:
+        >>> d = FlatDict(a=[])
+        >>> d.setattr("name", "Chang")
+        >>> c = d.empty_like()
+        >>> c.dict()
+        {}
+        >>> c.getattr("name")
+        'Chang'
+    """
+
+    empty = self.empty(*args, **kwargs)
+    empty.__dict__.update(self.__dict__)
+    return empty
 
@@ -5834,8 +5836,7 @@

Source code in chanfig/flat_dict.py -
Python
525
-526
+              
Python
526
 527
 528
 529
@@ -5870,43 +5871,44 @@ 

558 559 560 -561

@classmethod
-def from_dict(cls, obj: Mapping | Sequence) -> Any:  # pylint: disable=R0911
-    r"""
-    Convert `Mapping` or `Sequence` to `FlatDict`.
-
-    Examples:
-        >>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3})
-        FlatDict(
-          ('a'): 1
-          ('b'): 2
-          ('c'): 3
-        )
-        >>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)])
-        FlatDict(
-          ('a'): 1
-          ('b'): 2
-          ('c'): 3
-        )
-        >>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}])
-        [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]
-        >>> FlatDict.from_dict({1, 2, 3})
-        Traceback (most recent call last):
-        TypeError: Expected Mapping or Sequence, but got <class 'set'>.
-    """
-
-    if obj is None:
-        return cls()
-    if issubclass(cls, FlatDict):
-        cls = cls.empty  # type: ignore[assignment] # pylint: disable=W0642
-    if isinstance(obj, Mapping):
-        return cls(obj)
-    if isinstance(obj, Sequence):
-        try:
-            return cls(obj)
-        except ValueError:
-            return [cls(json) for json in obj]
-    raise TypeError(f"Expected Mapping or Sequence, but got {type(obj)}.")
+561
+562
@classmethod
+def from_dict(cls, obj: Mapping | Sequence) -> Any:  # pylint: disable=R0911
+    r"""
+    Convert `Mapping` or `Sequence` to `FlatDict`.
+
+    Examples:
+        >>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3})
+        FlatDict(
+          ('a'): 1
+          ('b'): 2
+          ('c'): 3
+        )
+        >>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)])
+        FlatDict(
+          ('a'): 1
+          ('b'): 2
+          ('c'): 3
+        )
+        >>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}])
+        [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]
+        >>> FlatDict.from_dict({1, 2, 3})
+        Traceback (most recent call last):
+        TypeError: Expected Mapping or Sequence, but got <class 'set'>.
+    """
+
+    if obj is None:
+        return cls()
+    if issubclass(cls, FlatDict):
+        cls = cls.empty  # type: ignore[assignment] # pylint: disable=W0642
+    if isinstance(obj, Mapping):
+        return cls(obj)
+    if isinstance(obj, Sequence):
+        try:
+            return cls(obj)
+        except ValueError:
+            return [cls(json) for json in obj]
+    raise TypeError(f"Expected Mapping or Sequence, but got {type(obj)}.")
 
@@ -5964,8 +5966,7 @@

Source code in chanfig/flat_dict.py -
Python
1173
-1174
+              
Python
1174
 1175
 1176
 1177
@@ -5984,27 +5985,28 @@ 

1190 1191 1192 -1193

@classmethod
-def from_json(cls, file: File, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    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:
-        (FlatDict):
-
-    Examples:
-        >>> d = FlatDict.from_json('tests/test.json')
-        >>> d.dict()
-        {'a': 1, 'b': 2, 'c': 3}
-    """
-
-    with cls.open(file) as fp:  # pylint: disable=C0103
-        if isinstance(file, (IOBase, IO)):
-            return cls.from_jsons(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]
-        return cls.from_jsons(fp.read(), *args, **kwargs)
+1193
+1194
@classmethod
+def from_json(cls, file: File, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    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:
+        (FlatDict):
+
+    Examples:
+        >>> d = FlatDict.from_json('tests/test.json')
+        >>> d.dict()
+        {'a': 1, 'b': 2, 'c': 3}
+    """
+
+    with cls.open(file) as fp:  # pylint: disable=C0103
+        if isinstance(file, (IOBase, IO)):
+            return cls.from_jsons(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]
+        return cls.from_jsons(fp.read(), *args, **kwargs)
 
@@ -6063,8 +6065,7 @@

Source code in chanfig/flat_dict.py -
Python
1212
-1213
+              
Python
1213
 1214
 1215
 1216
@@ -6080,24 +6081,25 @@ 

1226 1227 1228 -1229

@classmethod
-def from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Construct `FlatDict` from json string.
-
-    Returns:
-        (FlatDict):
-
-    Examples:
-        >>> FlatDict.from_jsons('{\n  "a": 1,\n  "b": 2,\n  "c": 3\n}').dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> FlatDict.from_jsons('[["a", 1], ["b", 2], ["c", 3]]').dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> FlatDict.from_jsons('[{"a": 1}, {"b": 2}, {"c": 3}]')
-        [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]
-    """
-
-    return cls.from_dict(json_loads(string, *args, **kwargs))
+1229
+1230
@classmethod
+def from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Construct `FlatDict` from json string.
+
+    Returns:
+        (FlatDict):
+
+    Examples:
+        >>> FlatDict.from_jsons('{\n  "a": 1,\n  "b": 2,\n  "c": 3\n}').dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> FlatDict.from_jsons('[["a", 1], ["b", 2], ["c", 3]]').dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> FlatDict.from_jsons('[{"a": 1}, {"b": 2}, {"c": 3}]')
+        [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]
+    """
+
+    return cls.from_dict(json_loads(string, *args, **kwargs))
 
@@ -6154,8 +6156,7 @@

Source code in chanfig/flat_dict.py -
Python
1246
-1247
+              
Python
1247
 1248
 1249
 1250
@@ -6174,27 +6175,28 @@ 

1263 1264 1265 -1266

@classmethod
-def from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    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:
-        (FlatDict):
-
-    Examples:
-        >>> FlatDict.from_yaml('tests/test.yaml').dict()
-        {'a': 1, 'b': 2, 'c': 3}
-    """
-
-    kwargs.setdefault("Loader", YamlLoader)
-    with cls.open(file) as fp:  # pylint: disable=C0103
-        if isinstance(file, (IOBase, IO)):
-            return cls.from_yamls(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]
-        return cls.from_dict(yaml_load(fp, *args, **kwargs))
+1266
+1267
@classmethod
+def from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    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:
+        (FlatDict):
+
+    Examples:
+        >>> FlatDict.from_yaml('tests/test.yaml').dict()
+        {'a': 1, 'b': 2, 'c': 3}
+    """
+
+    kwargs.setdefault("Loader", YamlLoader)
+    with cls.open(file) as fp:  # pylint: disable=C0103
+        if isinstance(file, (IOBase, IO)):
+            return cls.from_yamls(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]
+        return cls.from_dict(yaml_load(fp, *args, **kwargs))
 
@@ -6253,8 +6255,7 @@

Source code in chanfig/flat_dict.py -
Python
1284
-1285
+              
Python
1285
 1286
 1287
 1288
@@ -6271,25 +6272,26 @@ 

1299 1300 1301 -1302

@classmethod
-def from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Construct `FlatDict` from yaml string.
-
-    Returns:
-        (FlatDict):
-
-    Examples:
-        >>> FlatDict.from_yamls('a: 1\nb: 2\nc: 3\n').dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> FlatDict.from_yamls('- - a\n  - 1\n- - b\n  - 2\n- - c\n  - 3\n').dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> FlatDict.from_yamls('- a: 1\n- b: 2\n- c: 3\n')
-        [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]
-    """
-
-    kwargs.setdefault("Loader", SafeLoader)
-    return cls.from_dict(yaml_load(string, *args, **kwargs))
+1302
+1303
@classmethod
+def from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Construct `FlatDict` from yaml string.
+
+    Returns:
+        (FlatDict):
+
+    Examples:
+        >>> FlatDict.from_yamls('a: 1\nb: 2\nc: 3\n').dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> FlatDict.from_yamls('- - a\n  - 1\n- - b\n  - 2\n- - c\n  - 3\n').dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> FlatDict.from_yamls('- a: 1\n- b: 2\n- c: 3\n')
+        [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]
+    """
+
+    kwargs.setdefault("Loader", SafeLoader)
+    return cls.from_dict(yaml_load(string, *args, **kwargs))
 
@@ -6671,7 +6673,8 @@

403 404 405 -406

def getattr(self, name: str, default: Any = Null) -> Any:
+406
+407
def getattr(self, name: str, default: Any = Null) -> Any:
     r"""
     Get attribute of `FlatDict`.
 
@@ -6704,13 +6707,14 @@ 

try: if name in self.__dict__: return self.__dict__[name] - if name in self.__class__.__dict__: - return self.__class__.__dict__[name] - return super().getattr(name, default) # type: ignore[misc] - except AttributeError: - if default is not Null: - return default - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") from None + for cls in self.__class__.__mro__: + if name in cls.__dict__: + return cls.__dict__[name] + return super().getattr(name, default) # type: ignore[misc] + except AttributeError: + if default is not Null: + return default + raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") from None

@@ -6769,8 +6773,7 @@

Source code in chanfig/flat_dict.py -
Python
945
-946
+              
Python
946
 947
 948
 949
@@ -6789,27 +6792,28 @@ 

962 963 964 -965

def gpu(self) -> Self:  # pragma: no cover
-    r"""
-    Move all tensors to gpu.
-
-    Returns:
-        self:
-
-    **Alias**:
-
-    + `cuda`
-
-    Examples:
-        >>> import torch
-        >>> d = FlatDict(a=torch.tensor(1))
-        >>> d.gpu().dict()  # doctest: +SKIP
-        {'a': tensor(1, device='cuda:0')}
-        >>> d.cuda().dict()  # alias  # doctest: +SKIP
-        {'a': tensor(1, device='cuda:0')}
-    """
-
-    return self.to(TorchDevice("cuda"))
+965
+966
def gpu(self) -> Self:  # pragma: no cover
+    r"""
+    Move all tensors to gpu.
+
+    Returns:
+        self:
+
+    **Alias**:
+
+    + `cuda`
+
+    Examples:
+        >>> import torch
+        >>> d = FlatDict(a=torch.tensor(1))
+        >>> d.gpu().dict()  # doctest: +SKIP
+        {'a': tensor(1, device='cuda:0')}
+        >>> d.cuda().dict()  # alias  # doctest: +SKIP
+        {'a': tensor(1, device='cuda:0')}
+    """
+
+    return self.to(TorchDevice("cuda"))
 
@@ -6894,8 +6898,7 @@

Source code in chanfig/flat_dict.py -
Python
466
-467
+              
Python
467
 468
 469
 470
@@ -6919,32 +6922,33 @@ 

488 489 490 -491

def hasattr(self, name: str) -> bool:
-    r"""
-    Determine if an attribute exists in `FlatDict`.
-
-    Args:
-        name:
-
-    Returns:
-        (bool):
-
-    Examples:
-        >>> d = FlatDict()
-        >>> d.setattr('name', 'chang')
-        >>> d.hasattr('name')
-        True
-        >>> d.delattr('name')
-        >>> d.hasattr('name')
-        False
-    """
-
-    try:
-        if name in self.__dict__ or name in self.__class__.__dict__:
-            return True
-        return super().hasattr(name)  # type: ignore[misc]
-    except AttributeError:
-        return False
+491
+492
def hasattr(self, name: str) -> bool:
+    r"""
+    Determine if an attribute exists in `FlatDict`.
+
+    Args:
+        name:
+
+    Returns:
+        (bool):
+
+    Examples:
+        >>> d = FlatDict()
+        >>> d.setattr('name', 'chang')
+        >>> d.hasattr('name')
+        True
+        >>> d.delattr('name')
+        >>> d.hasattr('name')
+        False
+    """
+
+    try:
+        if name in self.__dict__ or name in self.__class__.__dict__:
+            return True
+        return super().hasattr(name)  # type: ignore[misc]
+    except AttributeError:
+        return False
 
@@ -6966,15 +6970,15 @@

Source code in chanfig/flat_dict.py -
Python
849
-850
+              
Python
def inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Alias of [`intersect`][chanfig.FlatDict.intersect].
-    """
-    return self.intersect(other, *args, **kwargs)
+853
+854
def inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Alias of [`intersect`][chanfig.FlatDict.intersect].
+    """
+    return self.intersect(other, *args, **kwargs)
 
@@ -7151,8 +7155,7 @@

Source code in chanfig/flat_dict.py -
Python
590
-591
+              
Python
591
 592
 593
 594
@@ -7254,110 +7257,111 @@ 

690 691 692 -693

def interpolate(  # pylint: disable=R0912
-    self, use_variable: bool = True, interpolators: MutableMapping | None = None, unsafe_eval: bool = False
-) -> Self:
-    r"""
-    Perform Variable interpolation.
-
-    Variable interpolation allows you to set the value of one key to be the value of another key easily.
-
-    Args:
-        use_variable: Whether to convert values to `Variable` objects.
-        interpolators: Mapping contains values for interpolation. Defaults to `self`.
-        unsafe_eval: Whether to evaluate interpolated values.
-
-    Raises:
-        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:
-        >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}")
-        >>> d.dict()
-        {'a': 1, 'b': '${a}', 'c': '${a}.${b}'}
-        >>> d.interpolate(unsafe_eval=True).dict()
-        {'a': 1, 'b': 1, 'c': 1.1}
-        >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}")
-        >>> d.dict()
-        {'a': 1, 'b': '${a}', 'c': '${a}.${b}'}
-        >>> d.interpolate().dict()
-        {'a': 1, 'b': 1, 'c': '1.1'}
-        >>> isinstance(d.a, Variable)
-        True
-        >>> d.a += 1
-        >>> d.dict()
-        {'a': 2, 'b': 2, 'c': '1.1'}
-        >>> d.a is d.b
-        True
-        >>> d.b is d.c
-        False
-        >>> d = FlatDict(a=1, b="${a}", c="${b}")
-        >>> d.dict()
-        {'a': 1, 'b': '${a}', 'c': '${b}'}
-        >>> d.interpolate(False).dict()
-        {'a': 1, 'b': 1, 'c': 1}
-        >>> isinstance(d.a, Variable)
-        False
-        >>> d.a += 1
-        >>> d.dict()
-        {'a': 2, 'b': 1, 'c': 1}
-        >>> d = FlatDict(a=1, b="${b}", c="${b}")
-        >>> d.interpolate().dict()
-        Traceback (most recent call last):
-        ValueError: Cannot interpolate b to itself.
-        >>> d = FlatDict(a="${b}", b="${c}", c="${d}", d="${a}")
-        >>> d.interpolate().dict()
-        Traceback (most recent call last):
-        ValueError: Circular reference found: a->b->c->d->a.
-        >>> d = FlatDict(a=1, b="${a}", c="${d}")
-        >>> d.interpolate().dict()
-        Traceback (most recent call last):
-        ValueError: d is not found in FlatDict(
-          ('a'): '1'
-          ('b'): '${a}'
-          ('c'): '${d}'
-        ).
-    """
-    # pylint: disable=C0103
-
-    interpolators = interpolators or self
-    placeholders: dict[str, list[str]] = {}
-    for key, value in self.all_items():
-        if isinstance(value, list):
-            for v in value:
-                self.find_placeholders(key, v, placeholders)
-        elif isinstance(value, Mapping):
-            for v in value.values():
-                self.find_placeholders(key, v, placeholders)
-        else:
-            self.find_placeholders(key, value, placeholders)
-    circular_references = find_circular_reference(placeholders)
-    if circular_references:
-        raise ValueError(f"Circular reference found: {'->'.join(circular_references)}.")
-    if use_variable:
-        placeholder_names = {i for j in placeholders.values() for i in j}
-        for name in list(placeholder_names.difference(placeholders.keys())):
-            if name not in interpolators:
-                raise ValueError(f"{name} is not found in {interpolators}.")
-            if not isinstance(interpolators[name], Variable):
-                interpolators[name] = Variable(interpolators[name])
-    for key, value in placeholders.items():
-        if isinstance(self[key], list):
-            for index, v in enumerate(self[key]):
-                self[key][index] = self.substitute(v, interpolators, value)
-        elif isinstance(self[key], Mapping):
-            for k, v in self[key].items():
-                self[key][k] = self.substitute(v, interpolators, value)
-        else:
-            self[key] = self.substitute(self[key], interpolators, value)
-        if unsafe_eval and isinstance(self[key], str):
-            with suppress(SyntaxError):
-                self[key] = eval(self[key])  # pylint: disable=W0123
-    return self
+693
+694
def interpolate(  # pylint: disable=R0912
+    self, use_variable: bool = True, interpolators: MutableMapping | None = None, unsafe_eval: bool = False
+) -> Self:
+    r"""
+    Perform Variable interpolation.
+
+    Variable interpolation allows you to set the value of one key to be the value of another key easily.
+
+    Args:
+        use_variable: Whether to convert values to `Variable` objects.
+        interpolators: Mapping contains values for interpolation. Defaults to `self`.
+        unsafe_eval: Whether to evaluate interpolated values.
+
+    Raises:
+        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:
+        >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}")
+        >>> d.dict()
+        {'a': 1, 'b': '${a}', 'c': '${a}.${b}'}
+        >>> d.interpolate(unsafe_eval=True).dict()
+        {'a': 1, 'b': 1, 'c': 1.1}
+        >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}")
+        >>> d.dict()
+        {'a': 1, 'b': '${a}', 'c': '${a}.${b}'}
+        >>> d.interpolate().dict()
+        {'a': 1, 'b': 1, 'c': '1.1'}
+        >>> isinstance(d.a, Variable)
+        True
+        >>> d.a += 1
+        >>> d.dict()
+        {'a': 2, 'b': 2, 'c': '1.1'}
+        >>> d.a is d.b
+        True
+        >>> d.b is d.c
+        False
+        >>> d = FlatDict(a=1, b="${a}", c="${b}")
+        >>> d.dict()
+        {'a': 1, 'b': '${a}', 'c': '${b}'}
+        >>> d.interpolate(False).dict()
+        {'a': 1, 'b': 1, 'c': 1}
+        >>> isinstance(d.a, Variable)
+        False
+        >>> d.a += 1
+        >>> d.dict()
+        {'a': 2, 'b': 1, 'c': 1}
+        >>> d = FlatDict(a=1, b="${b}", c="${b}")
+        >>> d.interpolate().dict()
+        Traceback (most recent call last):
+        ValueError: Cannot interpolate b to itself.
+        >>> d = FlatDict(a="${b}", b="${c}", c="${d}", d="${a}")
+        >>> d.interpolate().dict()
+        Traceback (most recent call last):
+        ValueError: Circular reference found: a->b->c->d->a.
+        >>> d = FlatDict(a=1, b="${a}", c="${d}")
+        >>> d.interpolate().dict()
+        Traceback (most recent call last):
+        ValueError: d is not found in FlatDict(
+          ('a'): '1'
+          ('b'): '${a}'
+          ('c'): '${d}'
+        ).
+    """
+    # pylint: disable=C0103
+
+    interpolators = interpolators or self
+    placeholders: dict[str, list[str]] = {}
+    for key, value in self.all_items():
+        if isinstance(value, list):
+            for v in value:
+                self.find_placeholders(key, v, placeholders)
+        elif isinstance(value, Mapping):
+            for v in value.values():
+                self.find_placeholders(key, v, placeholders)
+        else:
+            self.find_placeholders(key, value, placeholders)
+    circular_references = find_circular_reference(placeholders)
+    if circular_references:
+        raise ValueError(f"Circular reference found: {'->'.join(circular_references)}.")
+    if use_variable:
+        placeholder_names = {i for j in placeholders.values() for i in j}
+        for name in list(placeholder_names.difference(placeholders.keys())):
+            if name not in interpolators:
+                raise ValueError(f"{name} is not found in {interpolators}.")
+            if not isinstance(interpolators[name], Variable):
+                interpolators[name] = Variable(interpolators[name])
+    for key, value in placeholders.items():
+        if isinstance(self[key], list):
+            for index, v in enumerate(self[key]):
+                self[key][index] = self.substitute(v, interpolators, value)
+        elif isinstance(self[key], Mapping):
+            for k, v in self[key].items():
+                self[key][k] = self.substitute(v, interpolators, value)
+        else:
+            self[key] = self.substitute(self[key], interpolators, value)
+        if unsafe_eval and isinstance(self[key], str):
+            with suppress(SyntaxError):
+                self[key] = eval(self[key])  # pylint: disable=W0123
+    return self
 
@@ -7453,8 +7457,7 @@

Source code in chanfig/flat_dict.py -
Python
810
-811
+              
Python
811
 812
 813
 814
@@ -7490,44 +7493,45 @@ 

844 845 846 -847

def intersect(self, other: Mapping | Iterable | PathStr) -> Self:
-    r"""
-    Intersection of `FlatDict` and `other`.
-
-    Args:
-        other (Mapping | Iterable | PathStr):
-
-    Returns:
-        (FlatDict):
-
-    **Alias**:
-
-    + `inter`
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}
-        >>> d.intersect(n).dict()
-        {}
-        >>> l = [('c', 3), ('d', 4)]
-        >>> d.intersect(l).dict()
-        {'c': 3}
-        >>> d.merge(l).intersect("tests/test.yaml").dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> d.intersect(1)
-        Traceback (most recent call last):
-        TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.
-        >>> d.inter(FlatDict(b='b', c='c', d='d')).dict()  # alias
-        {}
-    """
-
-    if isinstance(other, (PathLike, str, bytes)):
-        other = self.load(other)
-    if isinstance(other, (Mapping,)):
-        other = self.empty(other).items()
-    if not isinstance(other, Iterable):
-        raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.")
-    return self.empty(**{key: value for key, value in other if key in self and self[key] == value})  # type: ignore
+847
+848
def intersect(self, other: Mapping | Iterable | PathStr) -> Self:
+    r"""
+    Intersection of `FlatDict` and `other`.
+
+    Args:
+        other (Mapping | Iterable | PathStr):
+
+    Returns:
+        (FlatDict):
+
+    **Alias**:
+
+    + `inter`
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}
+        >>> d.intersect(n).dict()
+        {}
+        >>> l = [('c', 3), ('d', 4)]
+        >>> d.intersect(l).dict()
+        {'c': 3}
+        >>> d.merge(l).intersect("tests/test.yaml").dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> d.intersect(1)
+        Traceback (most recent call last):
+        TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.
+        >>> d.inter(FlatDict(b='b', c='c', d='d')).dict()  # alias
+        {}
+    """
+
+    if isinstance(other, (PathLike, str, bytes)):
+        other = self.load(other)
+    if isinstance(other, (Mapping,)):
+        other = self.empty(other).items()
+    if not isinstance(other, Iterable):
+        raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.")
+    return self.empty(**{key: value for key, value in other if key in self and self[key] == value})  # type: ignore
 
@@ -7557,8 +7561,7 @@

Source code in chanfig/flat_dict.py -
Python
1158
-1159
+              
Python
1159
 1160
 1161
 1162
@@ -7570,20 +7573,21 @@ 

1168 1169 1170 -1171

def json(self, file: File, *args: Any, **kwargs: Any) -> None:
-    r"""
-    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:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.json("tests/test.json")
-    """
-
-    with self.open(file, mode="w") as fp:  # pylint: disable=C0103
-        fp.write(self.jsons(*args, **kwargs))
+1171
+1172
def json(self, file: File, *args: Any, **kwargs: Any) -> None:
+    r"""
+    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:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.json("tests/test.json")
+    """
+
+    with self.open(file, mode="w") as fp:  # pylint: disable=C0103
+        fp.write(self.jsons(*args, **kwargs))
 
@@ -7635,8 +7639,7 @@

Source code in chanfig/flat_dict.py -
Python
1195
-1196
+              
Python
1196
 1197
 1198
 1199
@@ -7650,22 +7653,23 @@ 

1207 1208 1209 -1210

def jsons(self, *args: Any, **kwargs: Any) -> str:
-    r"""
-    Dump `FlatDict` to json string.
-
-    Returns:
-        (str):
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.jsons()
-        '{\n  "a": 1,\n  "b": 2,\n  "c": 3\n}'
-    """
-
-    kwargs.setdefault("cls", JsonEncoder)
-    kwargs.setdefault("indent", self.getattr("indent", 2))
-    return json_dumps(self.dict(), *args, **kwargs)
+1210
+1211
def jsons(self, *args: Any, **kwargs: Any) -> str:
+    r"""
+    Dump `FlatDict` to json string.
+
+    Returns:
+        (str):
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.jsons()
+        '{\n  "a": 1,\n  "b": 2,\n  "c": 3\n}'
+    """
+
+    kwargs.setdefault("cls", JsonEncoder)
+    kwargs.setdefault("indent", self.getattr("indent", 2))
+    return json_dumps(self.dict(), *args, **kwargs)
 
@@ -7804,8 +7808,7 @@

Source code in chanfig/flat_dict.py -
Python
1116
-1117
+              
Python
1117
 1118
 1119
 1120
@@ -7844,47 +7847,48 @@ 

1153 1154 1155 -1156

@classmethod
-def load(  # pylint: disable=W1113
-    cls, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]
-) -> Self:
-    """
-    Load `FlatDict` from file.
-
-    Args:
-        file: File to load from.
-        method: File type, should be in `JSON` or `YAML`.
-
-    Returns:
-        (FlatDict):
-
-    Raises:
-        ValueError: If load from `IO` and `method` is not specified.
-        TypeError: If dump to unsupported extension.
-
-    Examples:
-        >>> d = FlatDict.load("tests/test.yaml")
-        >>> d.dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> d.load("tests/test.conf")
-        Traceback (most recent call last):
-        TypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.
-        >>> with open("tests/test.yaml") as f:
-        ...     d.load(f)
-        Traceback (most recent call last):
-        ValueError: `method` must be specified when loading from IO.
-    """
-
-    if method is None:
-        if isinstance(file, (IOBase, IO)):
-            raise ValueError("`method` must be specified when loading from IO.")
-        method = splitext(file)[-1][1:]
-    extension = method.lower()
-    if extension in JSON:
-        return cls.from_json(file, *args, **kwargs)
-    if extension in YAML:
-        return cls.from_yaml(file, *args, **kwargs)
-    raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.")
+1156
+1157
@classmethod
+def load(  # pylint: disable=W1113
+    cls, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]
+) -> Self:
+    """
+    Load `FlatDict` from file.
+
+    Args:
+        file: File to load from.
+        method: File type, should be in `JSON` or `YAML`.
+
+    Returns:
+        (FlatDict):
+
+    Raises:
+        ValueError: If load from `IO` and `method` is not specified.
+        TypeError: If dump to unsupported extension.
+
+    Examples:
+        >>> d = FlatDict.load("tests/test.yaml")
+        >>> d.dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> d.load("tests/test.conf")
+        Traceback (most recent call last):
+        TypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.
+        >>> with open("tests/test.yaml") as f:
+        ...     d.load(f)
+        Traceback (most recent call last):
+        ValueError: `method` must be specified when loading from IO.
+    """
+
+    if method is None:
+        if isinstance(file, (IOBase, IO)):
+            raise ValueError("`method` must be specified when loading from IO.")
+        method = splitext(file)[-1][1:]
+    extension = method.lower()
+    if extension in JSON:
+        return cls.from_json(file, *args, **kwargs)
+    if extension in YAML:
+        return cls.from_yaml(file, *args, **kwargs)
+    raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.")
 
@@ -8010,8 +8014,7 @@

Source code in chanfig/flat_dict.py -
Python
715
-716
+              
Python
716
 717
 718
 719
@@ -8058,55 +8061,56 @@ 

760 761 762 -763

def merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> Self:
-    r"""
-    Merge `other` into `FlatDict`.
-
-    Args:
-        *args: `Mapping` or `Sequence` to be merged.
-        overwrite: Whether to overwrite existing values.
-        **kwargs: `Mapping` to be merged.
-
-    Returns:
-        self:
-
-    **Alias**:
-
-    + `union`
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}
-        >>> d.merge(n).dict()
-        {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}
-        >>> l = [('c', 3), ('d', 4)]
-        >>> d.merge(l).dict()
-        {'a': 1, 'b': 'b', 'c': 3, 'd': 4}
-        >>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict()  # alias
-        {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}
-        >>> d = FlatDict()
-        >>> d.merge({1: 1, 2: 2, 3:3}).dict()
-        {1: 1, 2: 2, 3: 3}
-        >>> d.merge(d.clone()).dict()
-        {1: 1, 2: 2, 3: 3}
-        >>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict()
-        {1: 1, 2: 2, 3: 3, 4: 4, 5: 5}
-    """
-
-    if len(args) == 1:
-        args = args[0]
-        if isinstance(args, (PathLike, str, bytes)):
-            args = self.load(args)  # type: ignore[assignment]
-            warn(
-                "merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.",
-                PendingDeprecationWarning,
-            )
-        self._merge(self, args, overwrite=overwrite)
-    elif len(args) > 1:
-        self._merge(self, args, overwrite=overwrite)
-    if kwargs:
-        self._merge(self, kwargs, overwrite=overwrite)
-    return self
+763
+764
def merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> Self:
+    r"""
+    Merge `other` into `FlatDict`.
+
+    Args:
+        *args: `Mapping` or `Sequence` to be merged.
+        overwrite: Whether to overwrite existing values.
+        **kwargs: `Mapping` to be merged.
+
+    Returns:
+        self:
+
+    **Alias**:
+
+    + `union`
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}
+        >>> d.merge(n).dict()
+        {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}
+        >>> l = [('c', 3), ('d', 4)]
+        >>> d.merge(l).dict()
+        {'a': 1, 'b': 'b', 'c': 3, 'd': 4}
+        >>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict()  # alias
+        {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}
+        >>> d = FlatDict()
+        >>> d.merge({1: 1, 2: 2, 3:3}).dict()
+        {1: 1, 2: 2, 3: 3}
+        >>> d.merge(d.clone()).dict()
+        {1: 1, 2: 2, 3: 3}
+        >>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict()
+        {1: 1, 2: 2, 3: 3, 4: 4, 5: 5}
+    """
+
+    if len(args) == 1:
+        args = args[0]
+        if isinstance(args, (PathLike, str, bytes)):
+            args = self.load(args)  # type: ignore[assignment]
+            warn(
+                "merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.",
+                PendingDeprecationWarning,
+            )
+        self._merge(self, args, overwrite=overwrite)
+    elif len(args) > 1:
+        self._merge(self, args, overwrite=overwrite)
+    if kwargs:
+        self._merge(self, kwargs, overwrite=overwrite)
+    return self
 
@@ -8215,8 +8219,7 @@

Source code in chanfig/flat_dict.py -
Python
790
-791
+              
Python
791
 792
 793
 794
@@ -8233,25 +8236,26 @@ 

805 806 807 -808

def merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Merge content of `file` into `FlatDict`.
-
-    Args:
-        file (File):
-        *args: Passed to [`load`][chanfig.FlatDict.load].
-        **kwargs: Passed to [`load`][chanfig.FlatDict.load].
-
-    Returns:
-        self:
-
-    Examples:
-        >>> d = FlatDict(a=1, b=1)
-        >>> d.merge_from_file("tests/test.yaml").dict()
-        {'a': 1, 'b': 2, 'c': 3}
-    """
-
-    return self.merge(self.load(file, *args, **kwargs))
+808
+809
def merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Merge content of `file` into `FlatDict`.
+
+    Args:
+        file (File):
+        *args: Passed to [`load`][chanfig.FlatDict.load].
+        **kwargs: Passed to [`load`][chanfig.FlatDict.load].
+
+    Returns:
+        self:
+
+    Examples:
+        >>> d = FlatDict(a=1, b=1)
+        >>> d.merge_from_file("tests/test.yaml").dict()
+        {'a': 1, 'b': 2, 'c': 3}
+    """
+
+    return self.merge(self.load(file, *args, **kwargs))
 
@@ -8382,8 +8386,7 @@

Source code in chanfig/flat_dict.py -
Python
1304
-1305
+              
Python
1305
 1306
 1307
 1308
@@ -8431,56 +8434,57 @@ 

1350 1351 1352 -1353

@staticmethod
-@contextmanager
-def open(file: File, *args: Any, encoding: str = "utf-8", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]:
-    r"""
-    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.
-
-    Args:
-        file: File path or IO.
-        *args: Additional arguments passed to `open`.
-            Defaults to ().
-        **kwargs: Any
-            Additional keyword arguments passed to `open`.
-            Defaults to {}.
-
-    Yields:
-        (Generator[IOBase | IO, Any, Any]):
-
-    Examples:
-        >>> with FlatDict.open("tests/test.yaml") as fp:
-        ...     print(fp.read())
-        a: 1
-        b: 2
-        c: 3
-        <BLANKLINE>
-        >>> io = open("tests/test.yaml")
-        >>> with FlatDict.open(io) as fp:
-        ...     print(fp.read())
-        a: 1
-        b: 2
-        c: 3
-        <BLANKLINE>
-        >>> with FlatDict.open(123, mode="w") as fp:
-        ...     print(fp.read())
-        Traceback (most recent call last):
-        TypeError: expected str, bytes, os.PathLike, IO or IOBase, not int
-    """
-
-    if isinstance(file, (IOBase, IO)):
-        yield file
-    elif isinstance(file, (PathLike, str, bytes)):
-        try:
-            file = open(file, *args, encoding=encoding, **kwargs)  # type: ignore[call-overload] # noqa: SIM115
-            yield file  # type: ignore[misc]
-        finally:
-            with suppress(Exception):
-                file.close()  # type: ignore[union-attr]
-    else:
-        raise TypeError(f"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}")
+1353
+1354
@staticmethod
+@contextmanager
+def open(file: File, *args: Any, encoding: str = "utf-8", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]:
+    r"""
+    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.
+
+    Args:
+        file: File path or IO.
+        *args: Additional arguments passed to `open`.
+            Defaults to ().
+        **kwargs: Any
+            Additional keyword arguments passed to `open`.
+            Defaults to {}.
+
+    Yields:
+        (Generator[IOBase | IO, Any, Any]):
+
+    Examples:
+        >>> with FlatDict.open("tests/test.yaml") as fp:
+        ...     print(fp.read())
+        a: 1
+        b: 2
+        c: 3
+        <BLANKLINE>
+        >>> io = open("tests/test.yaml")
+        >>> with FlatDict.open(io) as fp:
+        ...     print(fp.read())
+        a: 1
+        b: 2
+        c: 3
+        <BLANKLINE>
+        >>> with FlatDict.open(123, mode="w") as fp:
+        ...     print(fp.read())
+        Traceback (most recent call last):
+        TypeError: expected str, bytes, os.PathLike, IO or IOBase, not int
+    """
+
+    if isinstance(file, (IOBase, IO)):
+        yield file
+    elif isinstance(file, (PathLike, str, bytes)):
+        try:
+            file = open(file, *args, encoding=encoding, **kwargs)  # type: ignore[call-overload] # noqa: SIM115
+            yield file  # type: ignore[misc]
+        finally:
+            with suppress(Exception):
+                file.close()  # type: ignore[union-attr]
+    else:
+        raise TypeError(f"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}")
 
@@ -8552,8 +8556,7 @@

Source code in chanfig/flat_dict.py -
Python
1071
-1072
+              
Python
1072
 1073
 1074
 1075
@@ -8587,42 +8590,43 @@ 

1103 1104 1105 -1106

def save(  # pylint: disable=W1113
-    self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]
-) -> None:
-    r"""
-    Save `FlatDict` to file.
-
-    Raises:
-        ValueError: If save to `IO` and `method` is not specified.
-        TypeError: If save to unsupported extension.
-
-    **Alias**:
-
-    + `save`
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.save("tests/test.yaml")
-        >>> d.save("test.conf")
-        Traceback (most recent call last):
-        TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.
-        >>> with open("test.yaml", "w") as f:
-        ...     d.save(f)
-        Traceback (most recent call last):
-        ValueError: `method` must be specified when saving to IO.
-    """
-
-    if method is None:
-        if isinstance(file, (IOBase, IO)):
-            raise ValueError("`method` must be specified when saving to IO.")
-        method = splitext(file)[-1][1:]
-    extension = method.lower()
-    if extension in YAML:
-        return self.yaml(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026
-    if extension in JSON:
-        return self.json(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026
-    raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.")
+1106
+1107
def save(  # pylint: disable=W1113
+    self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]
+) -> None:
+    r"""
+    Save `FlatDict` to file.
+
+    Raises:
+        ValueError: If save to `IO` and `method` is not specified.
+        TypeError: If save to unsupported extension.
+
+    **Alias**:
+
+    + `save`
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.save("tests/test.yaml")
+        >>> d.save("test.conf")
+        Traceback (most recent call last):
+        TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.
+        >>> with open("test.yaml", "w") as f:
+        ...     d.save(f)
+        Traceback (most recent call last):
+        ValueError: `method` must be specified when saving to IO.
+    """
+
+    if method is None:
+        if isinstance(file, (IOBase, IO)):
+            raise ValueError("`method` must be specified when saving to IO.")
+        method = splitext(file)[-1][1:]
+    extension = method.lower()
+    if extension in YAML:
+        return self.yaml(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026
+    if extension in JSON:
+        return self.json(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026
+    raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.")
 
@@ -8858,8 +8862,7 @@

Source code in chanfig/flat_dict.py -
Python
408
-409
+              
Python
409
 410
 411
 412
@@ -8892,41 +8895,42 @@ 

439 440 441 -442

def setattr(self, name: str, value: Any) -> None:
-    r"""
-    Set attribute of `FlatDict`.
-
-    Note that it won't alter values in `FlatDict`.
-
-    Args:
-        name:
-        value:
-
-    Warns:
-        RuntimeWarning: If name already exists in `FlatDict`.
-
-    Examples:
-        >>> d = FlatDict()
-        >>> d.setattr('attr', 'value')
-        >>> d.getattr('attr')
-        'value'
-        >>> d.set('d', 1013)
-        >>> d.setattr('d', 1031)  # RuntimeWarning: d already exists in FlatDict.
-        >>> d.get('d')
-        1013
-        >>> d.d
-        1013
-        >>> d.getattr('d')
-        1031
-    """
-
-    if name in self:
-        warn(
-            f"{name} already exists in {self.__class__.__name__}.\n"
-            f"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.",
-            RuntimeWarning,
-        )
-    self.__dict__[name] = value
+442
+443
def setattr(self, name: str, value: Any) -> None:
+    r"""
+    Set attribute of `FlatDict`.
+
+    Note that it won't alter values in `FlatDict`.
+
+    Args:
+        name:
+        value:
+
+    Warns:
+        RuntimeWarning: If name already exists in `FlatDict`.
+
+    Examples:
+        >>> d = FlatDict()
+        >>> d.setattr('attr', 'value')
+        >>> d.getattr('attr')
+        'value'
+        >>> d.set('d', 1013)
+        >>> d.setattr('d', 1031)  # RuntimeWarning: d already exists in FlatDict.
+        >>> d.get('d')
+        1013
+        >>> d.d
+        1013
+        >>> d.getattr('d')
+        1031
+    """
+
+    if name in self:
+        warn(
+            f"{name} already exists in {self.__class__.__name__}.\n"
+            f"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.",
+            RuntimeWarning,
+        )
+    self.__dict__[name] = value
 
@@ -8986,8 +8990,7 @@

Source code in chanfig/flat_dict.py -
Python
563
-564
+              
Python
564
 565
 566
 567
@@ -9011,32 +9014,33 @@ 

585 586 587 -588

def sort(self, key: Callable | None = None, reverse: bool = False) -> Self:
-    r"""
-    Sort `FlatDict`.
-
-    Returns:
-        (FlatDict):
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.sort().dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> d = FlatDict(b=2, c=3, a=1)
-        >>> d.sort().dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> a = [1]
-        >>> d = FlatDict(z=0, a=a)
-        >>> a.append(2)
-        >>> d.sort().dict()
-        {'a': [1, 2], 'z': 0}
-    """
-
-    items = sorted(self.items(), key=key, reverse=reverse)
-    self.clear()
-    for k, v in items:  # pylint: disable=C0103
-        self[k] = v
-    return self
+588
+589
def sort(self, key: Callable | None = None, reverse: bool = False) -> Self:
+    r"""
+    Sort `FlatDict`.
+
+    Returns:
+        (FlatDict):
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.sort().dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> d = FlatDict(b=2, c=3, a=1)
+        >>> d.sort().dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> a = [1]
+        >>> d = FlatDict(z=0, a=a)
+        >>> a.append(2)
+        >>> d.sort().dict()
+        {'a': [1, 2], 'z': 0}
+    """
+
+    items = sorted(self.items(), key=key, reverse=reverse)
+    self.clear()
+    for k, v in items:  # pylint: disable=C0103
+        self[k] = v
+    return self
 
@@ -9118,8 +9122,7 @@

Source code in chanfig/flat_dict.py -
Python
902
-903
+              
Python
903
 904
 905
 906
@@ -9143,32 +9146,33 @@ 

924 925 926 -927

def to(self, cls: str | TorchDevice | TorchDType) -> Self:  # pragma: no cover
-    r"""
-    Convert values of `FlatDict` to target `cls`.
-
-    Args:
-        cls (str | torch.device | torch.dtype):
-
-    Returns:
-        self:
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.to(int)
-        Traceback (most recent call last):
-        TypeError: to() only support torch.dtype and torch.device, but got <class 'int'>.
-    """
-
-    # pylint: disable=C0103
-
-    if isinstance(cls, (str, TorchDevice, TorchDType)):
-        for k, v in self.all_items():
-            if hasattr(v, "to"):
-                self[k] = v.to(cls)
-        return self
-
-    raise TypeError(f"to() only support torch.dtype and torch.device, but got {cls}.")
+927
+928
def to(self, cls: str | TorchDevice | TorchDType) -> Self:  # pragma: no cover
+    r"""
+    Convert values of `FlatDict` to target `cls`.
+
+    Args:
+        cls (str | torch.device | torch.dtype):
+
+    Returns:
+        self:
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.to(int)
+        Traceback (most recent call last):
+        TypeError: to() only support torch.dtype and torch.device, but got <class 'int'>.
+    """
+
+    # pylint: disable=C0103
+
+    if isinstance(cls, (str, TorchDevice, TorchDType)):
+        for k, v in self.all_items():
+            if hasattr(v, "to"):
+                self[k] = v.to(cls)
+        return self
+
+    raise TypeError(f"to() only support torch.dtype and torch.device, but got {cls}.")
 
@@ -9190,17 +9194,17 @@

Source code in chanfig/flat_dict.py -
Python
518
-519
+              
Python
def to_dict(self, flatten: bool = False) -> Mapping | Sequence | Set:
-    r"""
-    Alias of [`dict`][chanfig.FlatDict.dict].
-    """
-
-    return self.dict(flatten)
+523
+524
def to_dict(self, flatten: bool = False) -> Mapping | Sequence | Set:
+    r"""
+    Alias of [`dict`][chanfig.FlatDict.dict].
+    """
+
+    return self.dict(flatten)
 
@@ -9259,8 +9263,7 @@

Source code in chanfig/flat_dict.py -
Python
973
-974
+              
Python
974
 975
 976
 977
@@ -9279,27 +9282,28 @@ 

990 991 992 -993

def tpu(self) -> Self:  # pragma: no cover
-    r"""
-    Move all tensors to tpu.
-
-    Returns:
-        self:
-
-    **Alias**:
-
-    + `xla`
-
-    Examples:
-        >>> import torch
-        >>> d = FlatDict(a=torch.tensor(1))
-        >>> d.tpu().dict()  # doctest: +SKIP
-        {'a': tensor(1, device='xla:0')}
-        >>> d.xla().dict()  # alias  # doctest: +SKIP
-        {'a': tensor(1, device='xla:0')}
-    """
-
-    return self.to(TorchDevice("xla"))
+993
+994
def tpu(self) -> Self:  # pragma: no cover
+    r"""
+    Move all tensors to tpu.
+
+    Returns:
+        self:
+
+    **Alias**:
+
+    + `xla`
+
+    Examples:
+        >>> import torch
+        >>> d = FlatDict(a=torch.tensor(1))
+        >>> d.tpu().dict()  # doctest: +SKIP
+        {'a': tensor(1, device='xla:0')}
+        >>> d.xla().dict()  # alias  # doctest: +SKIP
+        {'a': tensor(1, device='xla:0')}
+    """
+
+    return self.to(TorchDevice("xla"))
 
@@ -9321,15 +9325,15 @@

Source code in chanfig/flat_dict.py -
Python
784
-785
+              
Python
def union(self, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Alias of [`merge`][chanfig.FlatDict.merge].
-    """
-    return self.merge(*args, **kwargs)
+788
+789
def union(self, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Alias of [`merge`][chanfig.FlatDict.merge].
+    """
+    return self.merge(*args, **kwargs)
 
@@ -9465,15 +9469,15 @@

Source code in chanfig/flat_dict.py -
Python
def xla(self) -> Self:  # pragma: no cover
-    r"""
-    Alias of [`tpu`][chanfig.FlatDict.tpu].
-    """
-    return self.tpu()
+              
Python
def xla(self) -> Self:  # pragma: no cover
+    r"""
+    Alias of [`tpu`][chanfig.FlatDict.tpu].
+    """
+    return self.tpu()
 
@@ -9503,8 +9507,7 @@

Source code in chanfig/flat_dict.py -
Python
1231
-1232
+              
Python
1232
 1233
 1234
 1235
@@ -9516,20 +9519,21 @@ 

1241 1242 1243 -1244

def yaml(self, file: File, *args: Any, **kwargs: Any) -> None:
-    r"""
-    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:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.yaml("tests/test.yaml")
-    """
-
-    with self.open(file, mode="w") as fp:  # pylint: disable=C0103
-        self.yamls(fp, *args, **kwargs)
+1244
+1245
def yaml(self, file: File, *args: Any, **kwargs: Any) -> None:
+    r"""
+    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:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.yaml("tests/test.yaml")
+    """
+
+    with self.open(file, mode="w") as fp:  # pylint: disable=C0103
+        self.yamls(fp, *args, **kwargs)
 
@@ -9580,8 +9584,7 @@

Source code in chanfig/flat_dict.py -
Python
1268
-1269
+              
Python
1269
 1270
 1271
 1272
@@ -9594,21 +9597,22 @@ 

1279 1280 1281 -1282

def yamls(self, *args: Any, **kwargs: Any) -> str:
-    r"""
-    Dump `FlatDict` to yaml string.
-
-    Returns:
-        (str):
-
-    Examples:
-        >>> FlatDict(a=1, b=2, c=3).yamls()
-        'a: 1\nb: 2\nc: 3\n'
-    """
-
-    kwargs.setdefault("Dumper", YamlDumper)
-    kwargs.setdefault("indent", self.getattr("indent", 2))
-    return yaml_dump(self.dict(), *args, **kwargs)
+1282
+1283
def yamls(self, *args: Any, **kwargs: Any) -> str:
+    r"""
+    Dump `FlatDict` to yaml string.
+
+    Returns:
+        (str):
+
+    Examples:
+        >>> FlatDict(a=1, b=2, c=3).yamls()
+        'a: 1\nb: 2\nc: 3\n'
+    """
+
+    kwargs.setdefault("Dumper", YamlDumper)
+    kwargs.setdefault("indent", self.getattr("indent", 2))
+    return yaml_dump(self.dict(), *args, **kwargs)
 
diff --git a/nested_dict/index.html b/nested_dict/index.html index c075b468..8f16112c 100644 --- a/nested_dict/index.html +++ b/nested_dict/index.html @@ -2282,7 +2282,7 @@

NestedDict delimiter = self.getattr("delimiter", ".") if convert_mapping is None: convert_mapping = self.getattr("convert_mapping", False) - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty try: while isinstance(name, str) and delimiter in name: name, rest = name.split(delimiter, 1) @@ -2296,7 +2296,7 @@

NestedDict else: self, name = self[name], rest if isinstance(self, NestedDict): - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty except (AttributeError, TypeError): raise KeyError(name) from None @@ -2449,7 +2449,7 @@

NestedDict delimiter = self.getattr("delimiter", ".") if convert_mapping is None: convert_mapping = self.getattr("convert_mapping", False) - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty try: while isinstance(name, str) and delimiter in name: name, rest = name.split(delimiter, 1) @@ -2463,7 +2463,7 @@

NestedDict else: self, name = self[name], rest if isinstance(self, NestedDict): - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty except (AttributeError, TypeError): raise KeyError(name) from None @@ -4477,7 +4477,7 @@

delimiter = self.getattr("delimiter", ".") if convert_mapping is None: convert_mapping = self.getattr("convert_mapping", False) - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty try: while isinstance(name, str) and delimiter in name: name, rest = name.split(delimiter, 1) @@ -4491,7 +4491,7 @@

else: self, name = self[name], rest if isinstance(self, NestedDict): - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty except (AttributeError, TypeError): raise KeyError(name) from None @@ -4745,7 +4745,7 @@

delimiter = self.getattr("delimiter", ".") if convert_mapping is None: convert_mapping = self.getattr("convert_mapping", False) - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty try: while isinstance(name, str) and delimiter in name: name, rest = name.split(delimiter, 1) @@ -4759,7 +4759,7 @@

else: self, name = self[name], rest if isinstance(self, NestedDict): - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty except (AttributeError, TypeError): raise KeyError(name) from None diff --git a/registry/index.html b/registry/index.html index 11e31447..b4befc03 100644 --- a/registry/index.html +++ b/registry/index.html @@ -1157,7 +1157,13 @@

Source code in chanfig/registry.py -
Python
286
+                
Python
280
+281
+282
+283
+284
+285
+286
 287
 288
 289
@@ -1296,158 +1302,152 @@ 

422 423 424 -425 -426 -427 -428 -429 -430 -431

class ConfigRegistry(Registry):
-    """
-    `ConfigRegistry` for components that can be initialised with a `config`.
+425
class ConfigRegistry(Registry):
+    """
+    `ConfigRegistry` for components that can be initialised with a `config`.
+
+    `ConfigRegistry` is particularly useful when you want to construct a component from a configuration, such as a
+    Hugginface Transformers model.
+
+    See Also:
+        [`Registry`][chanfig.Registry]: General purpose Registry.
 
-    `ConfigRegistry` is particularly useful when you want to construct a component from a configuration, such as a
-    Hugginface Transformers model.
-
-    See Also:
-        [`Registry`][chanfig.Registry]: General purpose Registry.
-
-    Examples:
-        >>> from dataclasses import dataclass, field
-        >>> @dataclass
-        ... class Config:
-        ...     a: int
-        ...     b: int
-        ...     mode: str = "proj"
-        >>> registry = ConfigRegistry(key="mode")
-        >>> @registry.register("proj")
-        ... class Proj:
-        ...     def __init__(self, config):
-        ...         self.a = config.a
-        ...         self.b = config.b
-        >>> @registry.register("inv")
-        ... class Inv:
-        ...     def __init__(self, config):
-        ...         self.a = config.b
-        ...         self.b = config.a
-        >>> registry
-        ConfigRegistry(
-          ('proj'): <class 'chanfig.registry.Proj'>
-          ('inv'): <class 'chanfig.registry.Inv'>
-        )
-        >>> config = Config(a=0, b=1)
-        >>> module = registry.build(config)
-        >>> module.a, module.b
-        (0, 1)
-        >>> config = Config(a=0, b=1, mode="inv")
-        >>> module = registry.build(config)
-        >>> module.a, module.b
-        (1, 0)
-        >>> @dataclass
-        ... class ModuleConfig:
-        ...     a: int = 0
-        ...     b: int = 1
-        ...     mode: str = "proj"
-        >>> @dataclass
-        ... class NestedConfig:
-        ...     module: ModuleConfig = field(default_factory=ModuleConfig)
-        >>> nested_registry = ConfigRegistry(key="module.mode")
-        >>> @nested_registry.register("proj")
-        ... class Proj:
-        ...     def __init__(self, config):
-        ...         self.a = config.module.a
-        ...         self.b = config.module.b
-        >>> @nested_registry.register("inv")
-        ... class Inv:
-        ...     def __init__(self, config):
-        ...         self.a = config.module.b
-        ...         self.b = config.module.a
-        >>> nested_config = NestedConfig()
-        >>> module = nested_registry.build(nested_config)
-        >>> module.a, module.b
-        (0, 1)
-    """
-
-    @staticmethod
-    def init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
-        r"""
-        Constructor of component.
-
-        Args:
-            cls: The component to construct.
-            *args: The arguments to pass to the component.
-            **kwargs: The keyword arguments to pass to the component.
-
-        Returns:
-            (Any):
-
-        Examples:
-            >>> class Module:
-            ...     def __init__(self, config, a=None, b=None):
-            ...         self.config = config
-            ...         self.a = config.a if a is None else a
-            ...         self.b = config.b if b is None else b
-            >>> config = NestedDict({"a": 0, "b": 1})
-            >>> module = ConfigRegistry.init(Module, config, b=2)
-            >>> module.a, module.b
-            (0, 2)
-        """
+    Examples:
+        >>> from dataclasses import dataclass, field
+        >>> @dataclass
+        ... class Config:
+        ...     a: int
+        ...     b: int
+        ...     mode: str = "proj"
+        >>> registry = ConfigRegistry(key="mode")
+        >>> @registry.register("proj")
+        ... class Proj:
+        ...     def __init__(self, config):
+        ...         self.a = config.a
+        ...         self.b = config.b
+        >>> @registry.register("inv")
+        ... class Inv:
+        ...     def __init__(self, config):
+        ...         self.a = config.b
+        ...         self.b = config.a
+        >>> registry
+        ConfigRegistry(
+          ('proj'): <class 'chanfig.registry.Proj'>
+          ('inv'): <class 'chanfig.registry.Inv'>
+        )
+        >>> config = Config(a=0, b=1)
+        >>> module = registry.build(config)
+        >>> module.a, module.b
+        (0, 1)
+        >>> config = Config(a=0, b=1, mode="inv")
+        >>> module = registry.build(config)
+        >>> module.a, module.b
+        (1, 0)
+        >>> @dataclass
+        ... class ModuleConfig:
+        ...     a: int = 0
+        ...     b: int = 1
+        ...     mode: str = "proj"
+        >>> @dataclass
+        ... class NestedConfig:
+        ...     module: ModuleConfig = field(default_factory=ModuleConfig)
+        >>> nested_registry = ConfigRegistry(key="module.mode")
+        >>> @nested_registry.register("proj")
+        ... class Proj:
+        ...     def __init__(self, config):
+        ...         self.a = config.module.a
+        ...         self.b = config.module.b
+        >>> @nested_registry.register("inv")
+        ... class Inv:
+        ...     def __init__(self, config):
+        ...         self.a = config.module.b
+        ...         self.b = config.module.a
+        >>> nested_config = NestedConfig()
+        >>> module = nested_registry.build(nested_config)
+        >>> module.a, module.b
+        (0, 1)
+    """
+
+    @staticmethod
+    def init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
+        r"""
+        Constructor of component.
+
+        Args:
+            cls: The component to construct.
+            *args: The arguments to pass to the component.
+            **kwargs: The keyword arguments to pass to the component.
+
+        Returns:
+            (Any):
+
+        Examples:
+            >>> class Module:
+            ...     def __init__(self, config, a=None, b=None):
+            ...         self.config = config
+            ...         self.a = config.a if a is None else a
+            ...         self.b = config.b if b is None else b
+            >>> config = NestedDict({"a": 0, "b": 1})
+            >>> module = ConfigRegistry.init(Module, config, b=2)
+            >>> module.a, module.b
+            (0, 2)
+        """
+
+        return cls(config, *args, **kwargs)
+
+    def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]
+        r"""
+        Build a component.
 
-        return cls(config, *args, **kwargs)
-
-    def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]
-        r"""
-        Build a component.
+        Args:
+            config
+
+        Returns:
+            (Any):
 
-        Args:
-            config
+        Raises:
+            KeyError: If the component is not registered.
 
-        Returns:
-            (Any):
-
-        Raises:
-            KeyError: If the component is not registered.
-
-        Examples:
-            >>> from dataclasses import dataclass, field
-            >>> registry = ConfigRegistry(key="module.mode")
-            >>> @registry.register("proj")
-            ... class Proj:
-            ...     def __init__(self, config):
-            ...         self.a = config.module.a
-            ...         self.b = config.module.b
-            >>> @registry.register("inv")
-            ... class Inv:
-            ...     def __init__(self, config):
-            ...         self.a = config.module.b
-            ...         self.b = config.module.a
-            >>> @dataclass
-            ... class ModuleConfig:
-            ...     a: int = 0
-            ...     b: int = 1
-            ...     mode: str = "proj"
-            >>> @dataclass
-            ... class Config:
-            ...     module: ModuleConfig = field(default_factory=ModuleConfig)
-            >>> config = Config()
-            >>> module = registry.build(config)
-            >>> type(module)
-            <class 'chanfig.registry.Proj'>
-            >>> module.a, module.b
-            (0, 1)
-            >>> type(module)
-            <class 'chanfig.registry.Proj'>
-        """
-
-        key = self.getattr("key")
-        config_ = deepcopy(config)
-
-        while "." in key:
-            key, rest = key.split(".", 1)
-            config_, key = getattr(config_, key), rest
-        name = getattr(config_, key)
-
-        return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]
+        Examples:
+            >>> from dataclasses import dataclass, field
+            >>> registry = ConfigRegistry(key="module.mode")
+            >>> @registry.register("proj")
+            ... class Proj:
+            ...     def __init__(self, config):
+            ...         self.a = config.module.a
+            ...         self.b = config.module.b
+            >>> @registry.register("inv")
+            ... class Inv:
+            ...     def __init__(self, config):
+            ...         self.a = config.module.b
+            ...         self.b = config.module.a
+            >>> @dataclass
+            ... class ModuleConfig:
+            ...     a: int = 0
+            ...     b: int = 1
+            ...     mode: str = "proj"
+            >>> @dataclass
+            ... class Config:
+            ...     module: ModuleConfig = field(default_factory=ModuleConfig)
+            >>> config = Config()
+            >>> module = registry.build(config)
+            >>> type(module)
+            <class 'chanfig.registry.Proj'>
+            >>> module.a, module.b
+            (0, 1)
+            >>> type(module)
+            <class 'chanfig.registry.Proj'>
+        """
+
+        key = self.getattr("key")
+        config_ = deepcopy(config)
+
+        while "." in key:
+            key, rest = key.split(".", 1)
+            config_, key = getattr(config_, key), rest
+        name = getattr(config_, key)
+
+        return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]
 
@@ -1556,7 +1556,13 @@

Source code in chanfig/registry.py -
Python
379
+              
Python
373
+374
+375
+376
+377
+378
+379
 380
 381
 382
@@ -1602,65 +1608,59 @@ 

422 423 424 -425 -426 -427 -428 -429 -430 -431

def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]
-    r"""
-    Build a component.
+425
def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]
+    r"""
+    Build a component.
+
+    Args:
+        config
+
+    Returns:
+        (Any):
 
-    Args:
-        config
+    Raises:
+        KeyError: If the component is not registered.
 
-    Returns:
-        (Any):
-
-    Raises:
-        KeyError: If the component is not registered.
-
-    Examples:
-        >>> from dataclasses import dataclass, field
-        >>> registry = ConfigRegistry(key="module.mode")
-        >>> @registry.register("proj")
-        ... class Proj:
-        ...     def __init__(self, config):
-        ...         self.a = config.module.a
-        ...         self.b = config.module.b
-        >>> @registry.register("inv")
-        ... class Inv:
-        ...     def __init__(self, config):
-        ...         self.a = config.module.b
-        ...         self.b = config.module.a
-        >>> @dataclass
-        ... class ModuleConfig:
-        ...     a: int = 0
-        ...     b: int = 1
-        ...     mode: str = "proj"
-        >>> @dataclass
-        ... class Config:
-        ...     module: ModuleConfig = field(default_factory=ModuleConfig)
-        >>> config = Config()
-        >>> module = registry.build(config)
-        >>> type(module)
-        <class 'chanfig.registry.Proj'>
-        >>> module.a, module.b
-        (0, 1)
-        >>> type(module)
-        <class 'chanfig.registry.Proj'>
-    """
-
-    key = self.getattr("key")
-    config_ = deepcopy(config)
-
-    while "." in key:
-        key, rest = key.split(".", 1)
-        config_, key = getattr(config_, key), rest
-    name = getattr(config_, key)
-
-    return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]
+    Examples:
+        >>> from dataclasses import dataclass, field
+        >>> registry = ConfigRegistry(key="module.mode")
+        >>> @registry.register("proj")
+        ... class Proj:
+        ...     def __init__(self, config):
+        ...         self.a = config.module.a
+        ...         self.b = config.module.b
+        >>> @registry.register("inv")
+        ... class Inv:
+        ...     def __init__(self, config):
+        ...         self.a = config.module.b
+        ...         self.b = config.module.a
+        >>> @dataclass
+        ... class ModuleConfig:
+        ...     a: int = 0
+        ...     b: int = 1
+        ...     mode: str = "proj"
+        >>> @dataclass
+        ... class Config:
+        ...     module: ModuleConfig = field(default_factory=ModuleConfig)
+        >>> config = Config()
+        >>> module = registry.build(config)
+        >>> type(module)
+        <class 'chanfig.registry.Proj'>
+        >>> module.a, module.b
+        (0, 1)
+        >>> type(module)
+        <class 'chanfig.registry.Proj'>
+    """
+
+    key = self.getattr("key")
+    config_ = deepcopy(config)
+
+    while "." in key:
+        key, rest = key.split(".", 1)
+        config_, key = getattr(config_, key), rest
+    name = getattr(config_, key)
+
+    return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]
 
@@ -1779,7 +1779,13 @@

Source code in chanfig/registry.py -
Python
352
+              
Python
346
+347
+348
+349
+350
+351
+352
 353
 354
 355
@@ -1798,38 +1804,32 @@ 

368 369 370 -371 -372 -373 -374 -375 -376 -377

@staticmethod
-def init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
-    r"""
-    Constructor of component.
-
-    Args:
-        cls: The component to construct.
-        *args: The arguments to pass to the component.
-        **kwargs: The keyword arguments to pass to the component.
-
-    Returns:
-        (Any):
-
-    Examples:
-        >>> class Module:
-        ...     def __init__(self, config, a=None, b=None):
-        ...         self.config = config
-        ...         self.a = config.a if a is None else a
-        ...         self.b = config.b if b is None else b
-        >>> config = NestedDict({"a": 0, "b": 1})
-        >>> module = ConfigRegistry.init(Module, config, b=2)
-        >>> module.a, module.b
-        (0, 2)
-    """
-
-    return cls(config, *args, **kwargs)
+371
@staticmethod
+def init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
+    r"""
+    Constructor of component.
+
+    Args:
+        cls: The component to construct.
+        *args: The arguments to pass to the component.
+        **kwargs: The keyword arguments to pass to the component.
+
+    Returns:
+        (Any):
+
+    Examples:
+        >>> class Module:
+        ...     def __init__(self, config, a=None, b=None):
+        ...         self.config = config
+        ...         self.a = config.a if a is None else a
+        ...         self.b = config.b if b is None else b
+        >>> config = NestedDict({"a": 0, "b": 1})
+        >>> module = ConfigRegistry.init(Module, config, b=2)
+        >>> module.a, module.b
+        (0, 2)
+    """
+
+    return cls(config, *args, **kwargs)
 
@@ -2176,13 +2176,7 @@

274 275 276 -277 -278 -279 -280 -281 -282 -283

class Registry(NestedDict):
+277
class Registry(NestedDict):
     """
     `Registry` for components.
 
@@ -2260,183 +2254,177 @@ 

super().__init__(fallback=fallback) if override is not None: self.setattr("override", override) - else: - self.setattr("override", self.override) - if key is not None: - self.setattr("key", key) - else: - self.setattr("key", self.key) - if default is not None: - self.setattr("default", default) - else: - self.setattr("default", self.default) + if key is not None: + self.setattr("key", key) + if default is not None: + self.setattr("default", default) + + def register( + self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False + ) -> Callable: + r""" + Register a new component. - def register( - self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False - ) -> Callable: - r""" - Register a new component. - - Args: - component: The component to register. - name: The name of the component. - - Returns: - component: The registered component. - Registered component are expected to be `Callable`. - - Raises: - ValueError: If the component with the same name already registered and `Registry.override=False`. - - Examples: - >>> registry = Registry() - >>> @registry.register - ... @registry.register("Module1") - ... class Module: - ... def __init__(self, a, b): - ... self.a = a - ... self.b = b - >>> module = registry.register(Module, "Module2") - >>> registry - Registry( - ('Module1'): <class 'chanfig.registry.Module'> - ('Module'): <class 'chanfig.registry.Module'> - ('Module2'): <class 'chanfig.registry.Module'> - ) - """ - - if name in self and not (override or self.override): - raise ValueError(f"Component with name {name} already registered.") - - # Registry.register() - if name is not Null: - self.set(name, component) + Args: + component: The component to register. + name: The name of the component. + + Returns: + component: The registered component. + Registered component are expected to be `Callable`. + + Raises: + ValueError: If the component with the same name already registered and `Registry.override=False`. + + Examples: + >>> registry = Registry() + >>> @registry.register + ... @registry.register("Module1") + ... class Module: + ... def __init__(self, a, b): + ... self.a = a + ... self.b = b + >>> module = registry.register(Module, "Module2") + >>> registry + Registry( + ('Module1'): <class 'chanfig.registry.Module'> + ('Module'): <class 'chanfig.registry.Module'> + ('Module2'): <class 'chanfig.registry.Module'> + ) + """ + + if name in self and not (override or self.override): + raise ValueError(f"Component with name {name} already registered.") + + # Registry.register() + if name is not Null: + self.set(name, component) + if default: + self.setattr("default", component) + return component + # @Registry.register + if component is not Null and callable(component) and name is Null: + self.set(component.__name__, component) if default: self.setattr("default", component) return component - # @Registry.register - if component is not Null and callable(component) and name is Null: - self.set(component.__name__, component) - if default: - self.setattr("default", component) - return component - - # @Registry.register() - def decorator(name: Any = Null): - @wraps(self.register) - def wrapper(component): - if name is Null: - self.set(component.__name__, component) - else: - self.set(name, component) - if default: - self.setattr("default", component) - return component - - return wrapper + + # @Registry.register() + def decorator(name: Any = Null): + @wraps(self.register) + def wrapper(component): + if name is Null: + self.set(component.__name__, component) + else: + self.set(name, component) + if default: + self.setattr("default", component) + return component + + return wrapper + + return decorator(component) + + def lookup(self, name: str, default: Any = Null) -> Any: + r""" + Lookup for a component. - return decorator(component) - - def lookup(self, name: str, default: Any = Null) -> Any: - r""" - Lookup for a component. + Args: + name: + + Returns: + (Any): The component. - Args: - name: + Raises: + KeyError: If the component is not registered. - Returns: - (Any): The component. - - Raises: - KeyError: If the component is not registered. - - Examples: - >>> registry = Registry() - >>> @registry.register - ... class Module: - ... def __init__(self, a, b): - ... self.a = a - ... self.b = b - >>> registry.lookup("Module") - <class 'chanfig.registry.Module'> - """ - - if default is Null: - default = self.getattr("default", Null) - return self.get(name, default) - - @staticmethod - def init(cls: Callable, *args: Any, **kwargs: Any) -> Any: # pylint: disable=W0211 - r""" - Constructor of component. - - Args: - cls: The component to construct. - *args: The arguments to pass to the component. - **kwargs: The keyword arguments to pass to the component. - - Returns: - (Any): - - Examples: - >>> class Module: - ... def __init__(self, a, b): - ... self.a = a - ... self.b = b - >>> kwargs = {"a": 0, "b": 1} - >>> module = Registry.init(Module, **kwargs) - >>> type(module) - <class 'chanfig.registry.Module'> - >>> module.a, module.b - (0, 1) - """ + Examples: + >>> registry = Registry() + >>> @registry.register + ... class Module: + ... def __init__(self, a, b): + ... self.a = a + ... self.b = b + >>> registry.lookup("Module") + <class 'chanfig.registry.Module'> + """ + + if default is Null: + default = self.getattr("default", Null) + return self.get(name, default) + + @staticmethod + def init(cls: Callable, *args: Any, **kwargs: Any) -> Any: # pylint: disable=W0211 + r""" + Constructor of component. + + Args: + cls: The component to construct. + *args: The arguments to pass to the component. + **kwargs: The keyword arguments to pass to the component. + + Returns: + (Any): + + Examples: + >>> class Module: + ... def __init__(self, a, b): + ... self.a = a + ... self.b = b + >>> kwargs = {"a": 0, "b": 1} + >>> module = Registry.init(Module, **kwargs) + >>> type(module) + <class 'chanfig.registry.Module'> + >>> module.a, module.b + (0, 1) + """ + + return cls(*args, **kwargs) + + def build(self, name: str | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any: + r""" + Build a component. - return cls(*args, **kwargs) - - def build(self, name: str | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any: - r""" - Build a component. - - Args: - name (str | MutableMapping): - If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`. - Note that values in `kwargs` will override values in `name` if its a `MutableMapping`. - *args: The arguments to pass to the component. - **kwargs: The keyword arguments to pass to the component. + Args: + name (str | MutableMapping): + If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`. + Note that values in `kwargs` will override values in `name` if its a `MutableMapping`. + *args: The arguments to pass to the component. + **kwargs: The keyword arguments to pass to the component. + + Returns: + (Any): + + Raises: + KeyError: If the component is not registered. - Returns: - (Any): - - Raises: - KeyError: If the component is not registered. - - Examples: - >>> registry = Registry(key="model") - >>> @registry.register - ... class Module: - ... def __init__(self, a, b): - ... self.a = a - ... self.b = b - >>> config = {"module": {"model": "Module", "a": 1, "b": 2}} - >>> # registry.register(Module) - >>> module = registry.build(**config["module"]) - >>> type(module) - <class 'chanfig.registry.Module'> - >>> module.a - 1 - >>> module.b - 2 - >>> module = registry.build(config["module"], a=2) - >>> module.a - 2 - """ - - if isinstance(name, MutableMapping): - name = deepcopy(name) - name, kwargs = name.pop(self.getattr("key", "name")), dict(name, **kwargs) # type: ignore[arg-type] - if name is Null: - name, kwargs = kwargs.pop(self.getattr("key"), None), dict(**kwargs) - return self.init(self.lookup(name), *args, **kwargs) # type: ignore[arg-type] + Examples: + >>> registry = Registry(key="model") + >>> @registry.register + ... class Module: + ... def __init__(self, a, b): + ... self.a = a + ... self.b = b + >>> config = {"module": {"model": "Module", "a": 1, "b": 2}} + >>> # registry.register(Module) + >>> module = registry.build(**config["module"]) + >>> type(module) + <class 'chanfig.registry.Module'> + >>> module.a + 1 + >>> module.b + 2 + >>> module = registry.build(config["module"], a=2) + >>> module.a + 2 + """ + + if isinstance(name, MutableMapping): + name = deepcopy(name) + name, kwargs = name.pop(self.getattr("key", "name")), dict(name, **kwargs) # type: ignore[arg-type] + if name is Null: + name, kwargs = kwargs.pop(self.getattr("key"), None), dict(**kwargs) + return self.init(self.lookup(name), *args, **kwargs) # type: ignore[arg-type]

@@ -2593,7 +2581,13 @@

Source code in chanfig/registry.py -
Python
240
+              
Python
234
+235
+236
+237
+238
+239
+240
 241
 242
 243
@@ -2630,56 +2624,50 @@ 

274 275 276 -277 -278 -279 -280 -281 -282 -283

def build(self, name: str | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any:
-    r"""
-    Build a component.
-
-    Args:
-        name (str | MutableMapping):
-            If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`.
-            Note that values in `kwargs` will override values in `name` if its a `MutableMapping`.
-        *args: The arguments to pass to the component.
-        **kwargs: The keyword arguments to pass to the component.
+277
def build(self, name: str | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any:
+    r"""
+    Build a component.
+
+    Args:
+        name (str | MutableMapping):
+            If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`.
+            Note that values in `kwargs` will override values in `name` if its a `MutableMapping`.
+        *args: The arguments to pass to the component.
+        **kwargs: The keyword arguments to pass to the component.
+
+    Returns:
+        (Any):
+
+    Raises:
+        KeyError: If the component is not registered.
 
-    Returns:
-        (Any):
-
-    Raises:
-        KeyError: If the component is not registered.
-
-    Examples:
-        >>> registry = Registry(key="model")
-        >>> @registry.register
-        ... class Module:
-        ...     def __init__(self, a, b):
-        ...         self.a = a
-        ...         self.b = b
-        >>> config = {"module": {"model": "Module", "a": 1, "b": 2}}
-        >>> # registry.register(Module)
-        >>> module = registry.build(**config["module"])
-        >>> type(module)
-        <class 'chanfig.registry.Module'>
-        >>> module.a
-        1
-        >>> module.b
-        2
-        >>> module = registry.build(config["module"], a=2)
-        >>> module.a
-        2
-    """
-
-    if isinstance(name, MutableMapping):
-        name = deepcopy(name)
-        name, kwargs = name.pop(self.getattr("key", "name")), dict(name, **kwargs)  # type: ignore[arg-type]
-    if name is Null:
-        name, kwargs = kwargs.pop(self.getattr("key"), None), dict(**kwargs)
-    return self.init(self.lookup(name), *args, **kwargs)  # type: ignore[arg-type]
+    Examples:
+        >>> registry = Registry(key="model")
+        >>> @registry.register
+        ... class Module:
+        ...     def __init__(self, a, b):
+        ...         self.a = a
+        ...         self.b = b
+        >>> config = {"module": {"model": "Module", "a": 1, "b": 2}}
+        >>> # registry.register(Module)
+        >>> module = registry.build(**config["module"])
+        >>> type(module)
+        <class 'chanfig.registry.Module'>
+        >>> module.a
+        1
+        >>> module.b
+        2
+        >>> module = registry.build(config["module"], a=2)
+        >>> module.a
+        2
+    """
+
+    if isinstance(name, MutableMapping):
+        name = deepcopy(name)
+        name, kwargs = name.pop(self.getattr("key", "name")), dict(name, **kwargs)  # type: ignore[arg-type]
+    if name is Null:
+        name, kwargs = kwargs.pop(self.getattr("key"), None), dict(**kwargs)
+    return self.init(self.lookup(name), *args, **kwargs)  # type: ignore[arg-type]
 
@@ -2799,7 +2787,13 @@

Source code in chanfig/registry.py -
Python
212
+              
Python
206
+207
+208
+209
+210
+211
+212
 213
 214
 215
@@ -2819,39 +2813,33 @@ 

229 230 231 -232 -233 -234 -235 -236 -237 -238

@staticmethod
-def init(cls: Callable, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
-    r"""
-    Constructor of component.
-
-    Args:
-        cls: The component to construct.
-        *args: The arguments to pass to the component.
-        **kwargs: The keyword arguments to pass to the component.
-
-    Returns:
-        (Any):
-
-    Examples:
-        >>> class Module:
-        ...     def __init__(self, a, b):
-        ...         self.a = a
-        ...         self.b = b
-        >>> kwargs = {"a": 0, "b": 1}
-        >>> module = Registry.init(Module, **kwargs)
-        >>> type(module)
-        <class 'chanfig.registry.Module'>
-        >>> module.a, module.b
-        (0, 1)
-    """
-
-    return cls(*args, **kwargs)
+232
@staticmethod
+def init(cls: Callable, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
+    r"""
+    Constructor of component.
+
+    Args:
+        cls: The component to construct.
+        *args: The arguments to pass to the component.
+        **kwargs: The keyword arguments to pass to the component.
+
+    Returns:
+        (Any):
+
+    Examples:
+        >>> class Module:
+        ...     def __init__(self, a, b):
+        ...         self.a = a
+        ...         self.b = b
+        >>> kwargs = {"a": 0, "b": 1}
+        >>> module = Registry.init(Module, **kwargs)
+        >>> type(module)
+        <class 'chanfig.registry.Module'>
+        >>> module.a, module.b
+        (0, 1)
+    """
+
+    return cls(*args, **kwargs)
 
@@ -2960,7 +2948,13 @@

Source code in chanfig/registry.py -
Python
184
+              
Python
178
+179
+180
+181
+182
+183
+184
 185
 186
 187
@@ -2980,39 +2974,33 @@ 

201 202 203 -204 -205 -206 -207 -208 -209 -210

def lookup(self, name: str, default: Any = Null) -> Any:
-    r"""
-    Lookup for a component.
+204
def lookup(self, name: str, default: Any = Null) -> Any:
+    r"""
+    Lookup for a component.
+
+    Args:
+        name:
+
+    Returns:
+        (Any): The component.
 
-    Args:
-        name:
+    Raises:
+        KeyError: If the component is not registered.
 
-    Returns:
-        (Any): The component.
-
-    Raises:
-        KeyError: If the component is not registered.
-
-    Examples:
-        >>> registry = Registry()
-        >>> @registry.register
-        ... class Module:
-        ...     def __init__(self, a, b):
-        ...         self.a = a
-        ...         self.b = b
-        >>> registry.lookup("Module")
-        <class 'chanfig.registry.Module'>
-    """
-
-    if default is Null:
-        default = self.getattr("default", Null)
-    return self.get(name, default)
+    Examples:
+        >>> registry = Registry()
+        >>> @registry.register
+        ... class Module:
+        ...     def __init__(self, a, b):
+        ...         self.a = a
+        ...         self.b = b
+        >>> registry.lookup("Module")
+        <class 'chanfig.registry.Module'>
+    """
+
+    if default is Null:
+        default = self.getattr("default", Null)
+    return self.get(name, default)
 
@@ -3142,7 +3130,13 @@

Source code in chanfig/registry.py -
Python
118
+              
Python
112
+113
+114
+115
+116
+117
+118
 119
 120
 121
@@ -3200,77 +3194,71 @@ 

173 174 175 -176 -177 -178 -179 -180 -181 -182

def register(
-    self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False
-) -> Callable:
-    r"""
-    Register a new component.
-
-    Args:
-        component: The component to register.
-        name: The name of the component.
-
-    Returns:
-        component: The registered component.
-            Registered component are expected to be `Callable`.
-
-    Raises:
-        ValueError: If the component with the same name already registered and `Registry.override=False`.
-
-    Examples:
-        >>> registry = Registry()
-        >>> @registry.register
-        ... @registry.register("Module1")
-        ... class Module:
-        ...     def __init__(self, a, b):
-        ...         self.a = a
-        ...         self.b = b
-        >>> module = registry.register(Module, "Module2")
-        >>> registry
-        Registry(
-          ('Module1'): <class 'chanfig.registry.Module'>
-          ('Module'): <class 'chanfig.registry.Module'>
-          ('Module2'): <class 'chanfig.registry.Module'>
-        )
-    """
-
-    if name in self and not (override or self.override):
-        raise ValueError(f"Component with name {name} already registered.")
-
-    # Registry.register()
-    if name is not Null:
-        self.set(name, component)
+176
def register(
+    self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False
+) -> Callable:
+    r"""
+    Register a new component.
+
+    Args:
+        component: The component to register.
+        name: The name of the component.
+
+    Returns:
+        component: The registered component.
+            Registered component are expected to be `Callable`.
+
+    Raises:
+        ValueError: If the component with the same name already registered and `Registry.override=False`.
+
+    Examples:
+        >>> registry = Registry()
+        >>> @registry.register
+        ... @registry.register("Module1")
+        ... class Module:
+        ...     def __init__(self, a, b):
+        ...         self.a = a
+        ...         self.b = b
+        >>> module = registry.register(Module, "Module2")
+        >>> registry
+        Registry(
+          ('Module1'): <class 'chanfig.registry.Module'>
+          ('Module'): <class 'chanfig.registry.Module'>
+          ('Module2'): <class 'chanfig.registry.Module'>
+        )
+    """
+
+    if name in self and not (override or self.override):
+        raise ValueError(f"Component with name {name} already registered.")
+
+    # Registry.register()
+    if name is not Null:
+        self.set(name, component)
+        if default:
+            self.setattr("default", component)
+        return component
+    # @Registry.register
+    if component is not Null and callable(component) and name is Null:
+        self.set(component.__name__, component)
         if default:
             self.setattr("default", component)
         return component
-    # @Registry.register
-    if component is not Null and callable(component) and name is Null:
-        self.set(component.__name__, component)
-        if default:
-            self.setattr("default", component)
-        return component
-
-    # @Registry.register()
-    def decorator(name: Any = Null):
-        @wraps(self.register)
-        def wrapper(component):
-            if name is Null:
-                self.set(component.__name__, component)
-            else:
-                self.set(name, component)
-            if default:
-                self.setattr("default", component)
-            return component
-
-        return wrapper
-
-    return decorator(component)
+
+    # @Registry.register()
+    def decorator(name: Any = Null):
+        @wraps(self.register)
+        def wrapper(component):
+            if name is Null:
+                self.set(component.__name__, component)
+            else:
+                self.set(name, component)
+            if default:
+                self.setattr("default", component)
+            return component
+
+        return wrapper
+
+    return decorator(component)
 
diff --git a/search/search_index.json b/search/search_index.json index 2ee98cbf..2c7fd23a 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"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 reuse 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 supports variable interpolation. Set a member\u2019s value to another member\u2019s name wrapped in ${}, then call interpolate method. The value of this member will be automatically replaced with the value of another member.

dict in Python is ordered since Python 3.7, but there isn\u2019t a built-in method to help you sort a dict. FlatDictsupports sort to help you manage your dict.

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 work better with DefaultDict.

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 Tensor. 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 determine the type by their extension and dump/load FlatDict to/from a file.

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

To facilitate 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 make 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.

ConfigRegistry is a subclass of Registry that is specifically designed for building objects from a Config or a dataclass. Just specify the key when creating the registry and pass config to the build method, and you will get the object you want.

"},{"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 supports type, choices, validator, and required to ensure the correctness of the value.

To make it even easier, Variable also support 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
# CHANfiG, Easier Configuration.\n# Copyright (c) 2022-Present, CHANfiG Contributors\n\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the following licenses:\n# - The Unlicense\n# - GNU Affero General Public License v3.0 or later\n# - GNU General Public License v2.0 or later\n# - BSD 4-Clause \"Original\" or \"Old\" License\n# - MIT License\n# - Apache License 2.0\n\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n# See the LICENSE file for more details.\n\nimport os\n\nfrom chanfig import Config, Variable, configclass\n\n\n@configclass\nclass DataloaderConfig:\n    batch_size: int = 64\n    num_workers: int = 4\n    pin_memory: bool = True\n\n\nclass TestConfig(Config):\n    def __init__(self):\n        super().__init__()\n        dropout = Variable(0.1)\n        self.name = \"CHANfiG\"\n        self.seed = 1013\n        self.activation = \"GELU\"\n        self.optim.lr = 1e-3\n        self.dataloader = DataloaderConfig()\n        self.model.encoder.num_layers = 6\n        self.model.decoder.num_layers = 6\n        self.model.dropout = dropout\n        self.model.encoder.dropout = dropout\n        self.model.decoder.dropout = dropout\n        self.add_argument(\"--batch_size\", dest=\"data.batch_size\")\n        self.add_argument(\"--lr\", dest=\"optim.lr\")\n\n    def post(self):\n        self.id = f\"{self.name}_{self.seed}\"\n\n\nif __name__ == \"__main__\":\n    # config = Config.load('config.yaml')  # read config from a yaml\n    # config = Config.load('config.json')  # read config from a json\n    existing_config = {\"model.encoder.num_layers\": 8}\n    config = TestConfig()\n    config.merge(existing_config)\n    # config.merge('dataset.yaml')  # merge config from a yaml\n    # config.merge('dataset.json', overwrite=False)  # merge config from a json\n    config = config.parse()\n    config.model.decoder.num_layers = 8\n    config.freeze()\n    print(config)\n    # main(config)\n    dir_path = os.path.dirname(os.path.realpath(__file__))\n    print(dir_path)\n    config.save(os.path.join(dir_path, \"config.yaml\"))  # save config to a yaml\n    config.save(os.path.join(dir_path, \"config.json\"))  # save config to 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:

yamljson YAML
activation: GELU\ndataloader:\n  batch_size: 64\n  num_workers: 4\n  pin_memory: true\nid: CHANfiG_1013\nmodel:\n  decoder:\n    dropout: 0.1\n    num_layers: 8\n  dropout: 0.1\n  encoder:\n    dropout: 0.1\n    num_layers: 8\nname: CHANfiG\noptim:\n  lr: 0.001\nseed: 1013\n
JSON
{\n  \"name\": \"CHANfiG\",\n  \"seed\": 1013,\n  \"activation\": \"GELU\",\n  \"optim\": {\n    \"lr\": 0.001\n  },\n  \"dataloader\": {\n    \"batch_size\": 64,\n    \"num_workers\": 4,\n    \"pin_memory\": true\n  },\n  \"model\": {\n    \"encoder\": {\n      \"num_layers\": 8,\n      \"dropout\": 0.1\n    },\n    \"decoder\": {\n      \"num_layers\": 8,\n      \"dropout\": 0.1\n    },\n    \"dropout\": 0.1\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 pypiInstall the latest version from source Bash
pip install chanfig\n
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:

The UnlicenseGNU Affero General Public License v3.0 or laterGNU General Public License v2.0 or laterBSD 4-Clause \u201cOriginal\u201d or \u201cOld\u201d LicenseMIT LicenseApache License 2.0 Text Only
This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <https://unlicense.org>\n
Text Only
                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<https://www.gnu.org/licenses/>.\n
Text Only
                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n
Text Only
BSD 4-Clause License\n\nCopyright (c) 2022-Present, CHANfiG Contributors\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n3. All advertising materials mentioning features or use of this software must\n   display the following acknowledgement:\n     This product includes software developed by [project].\n\n4. Neither the name of the copyright holder nor the names of its\n   contributors may be used to endorse or promote products derived from\n   this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER \"AS IS\" AND ANY EXPRESS OR\nIMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\nEVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\nOR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\nWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\nOTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\nADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n
Text Only
MIT License\n\nCopyright (c) 2022-Present, CHANfiG Contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n
Text Only
                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n

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):\n    r\"\"\"\n    `Config` is an extension of `NestedDict`.\n\n    The differences between `Config` and `NestedDict` lies in 3 aspects:\n\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\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\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\n    You could also manually call `boot` if you you don't parse command-line arguments.\n\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\n        A **frozen** `Config` does not have this behaviour and\n        will raises `KeyError` when accessing anything that does not exist.\n\n        It is recommended to call `config.freeze()` or `config.to(NestedDict)` to avoid this behaviour.\n\n    Attributes:\n        parser (ConfigParser): Parser for command-line arguments.\n        frozen (bool): If `True`, the config is frozen and cannot be altered.\n\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    \"\"\"\n\n    parser: None  # ConfigParser, Python 3.7 does not support forward reference\n    frozen: bool\n\n    def __init__(self, *args: Any, default_factory: Callable | None = None, **kwargs: Any):\n        if default_factory is None:\n            default_factory = Config\n        self.setattr(\"frozen\", False)\n        super().__init__(*args, default_factory=default_factory, **kwargs)\n\n    def copy_class_attributes(self, recursive: bool = True) -> Self:\n        r\"\"\"\n        Copy class attributes to instance.\n\n        Args:\n            recursive:\n\n        Returns:\n            self:\n\n        Examples:\n            >>> class Ancestor(Config):\n            ...     a = 1\n            >>> class Parent(Ancestor):\n            ...     b = 2\n            >>> class Child(Parent):\n            ...     c = 3\n            >>> c = Child()\n            >>> c\n            Child(<class 'chanfig.config.Config'>, )\n            >>> c.copy_class_attributes(recursive=False)\n            Child(<class 'chanfig.config.Config'>,('c'): 3)\n            >>> c.copy_class_attributes()  # doctest: +SKIP\n            Child(<class 'chanfig.config.Config'>,\n                ('a'): 1,\n                ('b'): 2,\n                ('c'): 3\n            )\n        \"\"\"\n\n        def copy_cls_attributes(cls: type) -> Mapping:\n            return {\n                k: v\n                for k, v in cls.__dict__.items()\n                if k not in self\n                and not k.startswith(\"__\")\n                and (not (isinstance(v, (property, staticmethod, classmethod)) or callable(v)))\n            }\n\n        if recursive:\n            for cls in self.__class__.__mro__:\n                if cls.__module__.startswith(\"chanfig\"):\n                    break\n                self.merge(copy_cls_attributes(cls), overwrite=False)\n        else:\n            self.merge(copy_cls_attributes(self.__class__), overwrite=False)\n        return self\n\n    def post(self) -> Self | None:\n        r\"\"\"\n        Post process of `Config`.\n\n        Some `Config` may need to do some post process after `Config` is initialised.\n        `post` is provided for this lazy-initialisation purpose.\n\n        By default, `post` calls `interpolate` to perform variable interpolation.\n\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\n        See Also:\n            [`boot`][chanfig.Config.boot]\n\n        Returns:\n            self:\n\n        Examples:\n            >>> c = Config()\n            >>> c.dne\n            Config(<class 'chanfig.config.Config'>, )\n            >>> c.post()\n            Config(\n              ('dne'): Config()\n            )\n            >>> c.dne2\n            Traceback (most recent call last):\n            AttributeError: 'Config' object has no attribute 'dne2'\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        \"\"\"\n\n        self.interpolate()\n        self.validate()\n        self.apply_(lambda c: c.setattr(\"default_factory\", None) if isinstance(c, Config) else None)\n        return self\n\n    def boot(self) -> Self:\n        r\"\"\"\n        Apply `post` recursively.\n\n        Sub-config may have their own `post` method.\n        `boot` is provided to apply `post` recursively.\n\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\n        See Also:\n            [`post`][chanfig.Config.post]\n\n        Returns:\n            self:\n\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        \"\"\"\n\n        for value in self.values():\n            if isinstance(value, Config):\n                value.boot()\n        self.post()\n        return self\n\n    def parse(\n        self,\n        args: Iterable[str] | None = None,\n        default_config: str | None = None,\n        no_default_config_action: str = \"raise\",\n        boot: bool = True,\n    ) -> Self:\n        r\"\"\"\n\n        Parse command-line arguments with `ConfigParser`.\n\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\n        By default, this method internally calls `Config.boot()`.\n        To disable this behaviour, set `boot` to `False`.\n\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\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\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        \"\"\"\n\n        if not self.hasattr(\"parser\"):\n            self.setattr(\"parser\", ConfigParser())\n        self.getattr(\"parser\").parse(args, self, default_config, no_default_config_action)\n        if boot:\n            self.boot()\n        return self\n\n    def parse_config(\n        self,\n        args: Iterable[str] | None = None,\n        default_config: str | None = None,\n        no_default_config_action: str = \"raise\",\n        boot: bool = True,\n    ) -> Self:\n        r\"\"\"\n\n        Parse command-line arguments with `ConfigParser`.\n\n        `parse_config` only parse command-line arguments that is in defined in `Config`.\n\n        By default, this method internally calls `Config.boot()`.\n        To disable this behaviour, set `boot` to `False`.\n\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\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\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        \"\"\"\n\n        if not self.hasattr(\"parser\"):\n            self.setattr(\"parser\", ConfigParser())\n        self.getattr(\"parser\").parse_config(args, self, default_config, no_default_config_action)\n        if boot:\n            self.boot()\n        return self\n\n    def add_argument(self, *args: Any, **kwargs: Any) -> None:\n        r\"\"\"\n        Add an argument to `ConfigParser`.\n\n        Note that value defined in `Config` will override the default value defined in `add_argument`.\n\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        \"\"\"\n\n        if not self.hasattr(\"parser\"):\n            self.setattr(\"parser\", ConfigParser())\n        return self.getattr(\"parser\").add_argument(*args, **kwargs)\n\n    def freeze(self, recursive: bool = True) -> Self:\n        r\"\"\"\n        Freeze `Config`.\n\n        Args:\n            recursive:\n\n        **Alias**:\n\n        + `lock`\n\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\n        @wraps(self.freeze)\n        def freeze(config: Config) -> None:\n            if isinstance(config, Config):\n                config.setattr(\"frozen\", True)\n\n        if recursive:\n            self.apply_(freeze)\n        else:\n            freeze(self)\n        return self\n\n    def lock(self, recursive: bool = True) -> Self:\n        r\"\"\"\n        Alias of [`freeze`][chanfig.Config.freeze].\n        \"\"\"\n        return self.freeze(recursive=recursive)\n\n    @contextmanager\n    def locked(self):\n        \"\"\"\n        Context manager which temporarily locks `Config`.\n\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        \"\"\"\n\n        was_frozen = self.getattr(\"frozen\", False)\n        try:\n            self.freeze()\n            yield self\n        finally:\n            if not was_frozen:\n                self.defrost()\n\n    def defrost(self, recursive: bool = True) -> Self:\n        r\"\"\"\n        Defrost `Config`.\n\n        Args:\n            recursive:\n\n        **Alias**:\n\n        + `unlock`\n\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\n        @wraps(self.defrost)\n        def defrost(config: Config) -> None:\n            if isinstance(config, Config):\n                config.setattr(\"frozen\", False)\n\n        if recursive:\n            self.apply_(defrost)\n        else:\n            defrost(self)\n        return self\n\n    def unlock(self, recursive: bool = True) -> Self:\n        r\"\"\"\n        Alias of [`defrost`][chanfig.Config.defrost].\n        \"\"\"\n        return self.defrost(recursive=recursive)\n\n    @contextmanager\n    def unlocked(self):\n        \"\"\"\n        Context manager which temporarily unlocks `Config`.\n\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        \"\"\"\n\n        was_frozen = self.getattr(\"frozen\", False)\n        try:\n            self.defrost()\n            yield self\n        finally:\n            if was_frozen:\n                self.freeze()\n\n    def get(self, name: Any, default: Any = None, fallback: bool | None = None) -> Any:\n        r\"\"\"\n        Get value from `Config`.\n\n        Note that `default` has higher priority than `default_factory`.\n\n        Args:\n            name:\n            default:\n\n        Returns:\n            value:\n                If `Config` does not contain `name`, return `default`.\n                If `default` is not specified, return `default_factory()`.\n\n        Raises:\n            KeyError: If `Config` does not contain `name` and `default`/`default_factory` is not specified.\n\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        \"\"\"\n\n        if not self.hasattr(\"default_factory\"):  # did not call super().__init__() in sub-class\n            self.setattr(\"default_factory\", Config)\n        if name in self or not self.getattr(\"frozen\", False):\n            return super().get(name, default, fallback)\n        raise KeyError(name)\n\n    @frozen_check\n    def set(\n        self,\n        name: Any,\n        value: Any,\n        convert_mapping: bool | None = None,\n    ) -> None:\n        r\"\"\"\n        Set value of `Config`.\n\n        Args:\n            name:\n            value:\n            convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n                Defaults to self.convert_mapping.\n\n        Raises:\n            ValueError: If `Config` is frozen.\n\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        \"\"\"\n\n        return super().set(name, value, convert_mapping)\n\n    @frozen_check\n    def delete(self, name: Any) -> None:\n        r\"\"\"\n        Delete value from `Config`.\n\n        Args:\n            name:\n\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        \"\"\"\n\n        super().delete(name)\n\n    @frozen_check\n    def pop(self, name: Any, default: Any = Null) -> Any:\n        r\"\"\"\n        Pop value from `Config`.\n\n        Args:\n            name:\n            default:\n\n        Returns:\n            value: If `Config` does not contain `name`, return `default`.\n\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        \"\"\"\n\n        return 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:\n    r\"\"\"\n    Add an argument to `ConfigParser`.\n\n    Note that value defined in `Config` will override the default value defined in `add_argument`.\n\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    \"\"\"\n\n    if not self.hasattr(\"parser\"):\n        self.setattr(\"parser\", ConfigParser())\n    return 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 Self

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) -> Self:\n    r\"\"\"\n    Apply `post` recursively.\n\n    Sub-config may have their own `post` method.\n    `boot` is provided to apply `post` recursively.\n\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\n    See Also:\n        [`post`][chanfig.Config.post]\n\n    Returns:\n        self:\n\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    \"\"\"\n\n    for value in self.values():\n        if isinstance(value, Config):\n            value.boot()\n    self.post()\n    return self\n
"},{"location":"config/#chanfig.config.Config.copy_class_attributes","title":"copy_class_attributes(recursive=True)","text":"

Copy class attributes to instance.

Parameters:

Name Type Description Default recursive bool True

Returns:

Name Type Description self Self

Examples:

Python Console Session
>>> class Ancestor(Config):\n...     a = 1\n>>> class Parent(Ancestor):\n...     b = 2\n>>> class Child(Parent):\n...     c = 3\n>>> c = Child()\n>>> c\nChild(<class 'chanfig.config.Config'>, )\n>>> c.copy_class_attributes(recursive=False)\nChild(<class 'chanfig.config.Config'>,('c'): 3)\n>>> c.copy_class_attributes()\nChild(<class 'chanfig.config.Config'>,\n    ('a'): 1,\n    ('b'): 2,\n    ('c'): 3\n)\n
Source code in chanfig/config.py Python
def copy_class_attributes(self, recursive: bool = True) -> Self:\n    r\"\"\"\n    Copy class attributes to instance.\n\n    Args:\n        recursive:\n\n    Returns:\n        self:\n\n    Examples:\n        >>> class Ancestor(Config):\n        ...     a = 1\n        >>> class Parent(Ancestor):\n        ...     b = 2\n        >>> class Child(Parent):\n        ...     c = 3\n        >>> c = Child()\n        >>> c\n        Child(<class 'chanfig.config.Config'>, )\n        >>> c.copy_class_attributes(recursive=False)\n        Child(<class 'chanfig.config.Config'>,('c'): 3)\n        >>> c.copy_class_attributes()  # doctest: +SKIP\n        Child(<class 'chanfig.config.Config'>,\n            ('a'): 1,\n            ('b'): 2,\n            ('c'): 3\n        )\n    \"\"\"\n\n    def copy_cls_attributes(cls: type) -> Mapping:\n        return {\n            k: v\n            for k, v in cls.__dict__.items()\n            if k not in self\n            and not k.startswith(\"__\")\n            and (not (isinstance(v, (property, staticmethod, classmethod)) or callable(v)))\n        }\n\n    if recursive:\n        for cls in self.__class__.__mro__:\n            if cls.__module__.startswith(\"chanfig\"):\n                break\n            self.merge(copy_cls_attributes(cls), overwrite=False)\n    else:\n        self.merge(copy_cls_attributes(self.__class__), overwrite=False)\n    return self\n
"},{"location":"config/#chanfig.config.Config.defrost","title":"defrost(recursive=True)","text":"

Defrost Config.

Parameters:

Name Type Description Default recursive bool True

Alias:

  • unlock

Examples:

Python Console Session
>>> c = Config(**{'i.d': 1013})\n>>> c.getattr('frozen')\nFalse\n>>> c.freeze().dict()\n{'i': {'d': 1013}}\n>>> c.getattr('frozen')\nTrue\n>>> c.defrost(recursive=False).dict()\n{'i': {'d': 1013}}\n>>> c.getattr('frozen')\nFalse\n>>> c.i.getattr('frozen')\nTrue\n>>> c.unlock().dict()  # alias\n{'i': {'d': 1013}}\n>>> c.i.getattr('frozen')\nFalse\n
Source code in chanfig/config.py Python
def defrost(self, recursive: bool = True) -> Self:\n    r\"\"\"\n    Defrost `Config`.\n\n    Args:\n        recursive:\n\n    **Alias**:\n\n    + `unlock`\n\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\n    @wraps(self.defrost)\n    def defrost(config: Config) -> None:\n        if isinstance(config, Config):\n            config.setattr(\"frozen\", False)\n\n    if recursive:\n        self.apply_(defrost)\n    else:\n        defrost(self)\n    return 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:\n    r\"\"\"\n    Delete value from `Config`.\n\n    Args:\n        name:\n\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    \"\"\"\n\n    super().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:

  • lock

Examples:

Python Console Session
>>> c = Config(**{'i.d': 1013})\n>>> c.getattr('frozen')\nFalse\n>>> c.freeze(recursive=False).dict()\n{'i': {'d': 1013}}\n>>> c.getattr('frozen')\nTrue\n>>> c.i.getattr('frozen')\nFalse\n>>> c.lock().dict()  # alias\n{'i': {'d': 1013}}\n>>> c.i.getattr('frozen')\nTrue\n
Source code in chanfig/config.py Python
def freeze(self, recursive: bool = True) -> Self:\n    r\"\"\"\n    Freeze `Config`.\n\n    Args:\n        recursive:\n\n    **Alias**:\n\n    + `lock`\n\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\n    @wraps(self.freeze)\n    def freeze(config: Config) -> None:\n        if isinstance(config, Config):\n            config.setattr(\"frozen\", True)\n\n    if recursive:\n        self.apply_(freeze)\n    else:\n        freeze(self)\n    return 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:\n    r\"\"\"\n    Get value from `Config`.\n\n    Note that `default` has higher priority than `default_factory`.\n\n    Args:\n        name:\n        default:\n\n    Returns:\n        value:\n            If `Config` does not contain `name`, return `default`.\n            If `default` is not specified, return `default_factory()`.\n\n    Raises:\n        KeyError: If `Config` does not contain `name` and `default`/`default_factory` is not specified.\n\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    \"\"\"\n\n    if not self.hasattr(\"default_factory\"):  # did not call super().__init__() in sub-class\n        self.setattr(\"default_factory\", Config)\n    if name in self or not self.getattr(\"frozen\", False):\n        return super().get(name, default, fallback)\n    raise 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) -> Self:\n    r\"\"\"\n    Alias of [`freeze`][chanfig.Config.freeze].\n    \"\"\"\n    return 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\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    \"\"\"\n\n    was_frozen = self.getattr(\"frozen\", False)\n    try:\n        self.freeze()\n        yield self\n    finally:\n        if not was_frozen:\n            self.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(\n    self,\n    args: Iterable[str] | None = None,\n    default_config: str | None = None,\n    no_default_config_action: str = \"raise\",\n    boot: bool = True,\n) -> Self:\n    r\"\"\"\n\n    Parse command-line arguments with `ConfigParser`.\n\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\n    By default, this method internally calls `Config.boot()`.\n    To disable this behaviour, set `boot` to `False`.\n\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\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\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    \"\"\"\n\n    if not self.hasattr(\"parser\"):\n        self.setattr(\"parser\", ConfigParser())\n    self.getattr(\"parser\").parse(args, self, default_config, no_default_config_action)\n    if boot:\n        self.boot()\n    return 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(\n    self,\n    args: Iterable[str] | None = None,\n    default_config: str | None = None,\n    no_default_config_action: str = \"raise\",\n    boot: bool = True,\n) -> Self:\n    r\"\"\"\n\n    Parse command-line arguments with `ConfigParser`.\n\n    `parse_config` only parse command-line arguments that is in defined in `Config`.\n\n    By default, this method internally calls `Config.boot()`.\n    To disable this behaviour, set `boot` to `False`.\n\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\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\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    \"\"\"\n\n    if not self.hasattr(\"parser\"):\n        self.setattr(\"parser\", ConfigParser())\n    self.getattr(\"parser\").parse_config(args, self, default_config, no_default_config_action)\n    if boot:\n        self.boot()\n    return 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:\n    r\"\"\"\n    Pop value from `Config`.\n\n    Args:\n        name:\n        default:\n\n    Returns:\n        value: If `Config` does not contain `name`, return `default`.\n\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    \"\"\"\n\n    return 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 Self | None

Examples:

Python Console Session
>>> c = Config()\n>>> c.dne\nConfig(<class 'chanfig.config.Config'>, )\n>>> c.post()\nConfig(\n  ('dne'): Config()\n)\n>>> c.dne2\nTraceback (most recent call last):\nAttributeError: 'Config' object has no attribute 'dne2'\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()\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) -> Self | None:\n    r\"\"\"\n    Post process of `Config`.\n\n    Some `Config` may need to do some post process after `Config` is initialised.\n    `post` is provided for this lazy-initialisation purpose.\n\n    By default, `post` calls `interpolate` to perform variable interpolation.\n\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\n    See Also:\n        [`boot`][chanfig.Config.boot]\n\n    Returns:\n        self:\n\n    Examples:\n        >>> c = Config()\n        >>> c.dne\n        Config(<class 'chanfig.config.Config'>, )\n        >>> c.post()\n        Config(\n          ('dne'): Config()\n        )\n        >>> c.dne2\n        Traceback (most recent call last):\n        AttributeError: 'Config' object has no attribute 'dne2'\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    \"\"\"\n\n    self.interpolate()\n    self.validate()\n    self.apply_(lambda c: c.setattr(\"default_factory\", None) if isinstance(c, Config) else None)\n    return 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(\n    self,\n    name: Any,\n    value: Any,\n    convert_mapping: bool | None = None,\n) -> None:\n    r\"\"\"\n    Set value of `Config`.\n\n    Args:\n        name:\n        value:\n        convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n            Defaults to self.convert_mapping.\n\n    Raises:\n        ValueError: If `Config` is frozen.\n\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    \"\"\"\n\n    return 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) -> Self:\n    r\"\"\"\n    Alias of [`defrost`][chanfig.Config.defrost].\n    \"\"\"\n    return 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\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    \"\"\"\n\n    was_frozen = self.getattr(\"frozen\", False)\n    try:\n        self.defrost()\n        yield self\n    finally:\n        if was_frozen:\n            self.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):\n    r\"\"\"\n    Decorator check if the object is frozen.\n    \"\"\"\n\n    @wraps(func)\n    def decorator(self, *args: Any, **kwargs: Any):\n        if self.getattr(\"frozen\", False):\n            raise ValueError(\"Attempting to alter a frozen config. Run config.defrost() to defrost first.\")\n        return func(self, *args, **kwargs)\n\n    return decorator\n
"},{"location":"configclass/","title":"configclass","text":""},{"location":"configclass/#chanfig.configclasses.configclass","title":"configclass(cls=None, recursive=False)","text":"

Construct a Config in dataclass style.

This decorator creates a Config instance with the provided class attributes.

See Also

dataclass

Parameters:

Name Type Description Default cls Type[Any]

The class to be enhanced, provided directly if no parentheses are used.

None recursive bool

If True, recursively copy class attributes. Only applicable if used with parentheses.

False

Returns:

Type Description

A modified class with Config functionalities or a decorator with bound parameters.

Examples:

Python Console Session
>>> @configclass\n... class DataloaderConfig:\n...     batch_size: int = 64\n...     num_workers: int = 4\n...     pin_memory: bool = True\n>>> config = DataloaderConfig()\n>>> print(config)\nDataloaderConfig(<class 'chanfig.config.Config'>,\n  ('batch_size'): 64\n  ('num_workers'): 4\n  ('pin_memory'): True\n)\n
Source code in chanfig/configclasses.py Python
def configclass(cls=None, recursive: bool = False):\n    \"\"\"\n    Construct a Config in [`dataclass`][dataclasses.dataclass] style.\n\n    This decorator creates a Config instance with the provided class attributes.\n\n    See Also:\n        [`dataclass`][dataclasses.dataclass]\n\n    Args:\n        cls (Type[Any]): The class to be enhanced, provided directly if no parentheses are used.\n        recursive (bool): If True, recursively copy class attributes. Only applicable if used with parentheses.\n\n    Returns:\n        A modified class with Config functionalities or a decorator with bound parameters.\n\n    Examples:\n        >>> @configclass\n        ... class DataloaderConfig:\n        ...     batch_size: int = 64\n        ...     num_workers: int = 4\n        ...     pin_memory: bool = True\n        >>> config = DataloaderConfig()\n        >>> print(config)\n        DataloaderConfig(<class 'chanfig.config.Config'>,\n          ('batch_size'): 64\n          ('num_workers'): 4\n          ('pin_memory'): True\n        )\n    \"\"\"\n\n    def decorator(cls: Type[Any]):\n        if not issubclass(cls, Config):\n            config_cls = type(cls.__name__, (Config, cls), dict(cls.__dict__))\n            cls = config_cls\n\n        cls_init = cls.__init__\n\n        @wraps(cls_init)\n        def init(self, *args, **kwargs):\n            cls_init(self)\n            self.copy_class_attributes(recursive=recursive)\n            self.merge(*args, **kwargs)\n\n        setattr(cls, \"__init__\", init)  # noqa: B010\n\n        return cls\n\n    if cls is None:\n        return decorator\n    else:\n        return decorator(cls)\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):\n    r\"\"\"\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\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\n    Note that just like `collections.defaultdict`, `default_factory()` is called without any arguments.\n\n    Attributes:\n        default_factory: Default factory for defaultdict behaviour.\n\n    Raises:\n        TypeError: If `default_factory` is not callable.\n\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\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    \"\"\"\n\n    default_factory: Optional[Callable] = None\n\n    def __init__(  # pylint: disable=W1113\n        self, default_factory: Callable | None = None, *args: Any, **kwargs: Any\n    ) -> None:\n        super().__init__(*args, **kwargs)\n        if default_factory is not None:\n            if callable(default_factory):\n                self.setattr(\"default_factory\", default_factory)\n            else:\n                raise TypeError(\n                    f\"`default_factory={default_factory}` must be Callable, but got {type(default_factory)}.\"\n                )\n\n    def __missing__(self, name: Any, default=Null) -> Any:  # pylint: disable=R1710\n        if default is Null:\n            if self.getattr(\"default_factory\", None) in (None, Null) or (name.startswith(\"__\") and name.endswith(\"__\")):\n                raise KeyError(name) from None\n            default = self.getattr(\"default_factory\")()\n        if isinstance(default, FlatDict):\n            default.__dict__.update(self.__dict__)\n        super().set(name, default)\n        return default\n\n    def __repr__(self) -> str:\n        if self.default_factory is None:\n            return super().__repr__()\n        super_repr = super().__repr__()[len(self.__class__.__name__) :]  # noqa: E203\n        if len(super_repr) == 2:\n            return f\"{self.__class__.__name__}({self.default_factory}, )\"\n        return f\"{self.__class__.__name__}({self.default_factory},\" + super_repr[1:]\n\n    def add(self, name: Any):\n        r\"\"\"\n        Add a new default factory to the dictionary.\n\n        Args:\n            name:\n\n        Raises:\n            ValueError: If `default_factory` is None.\n\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        \"\"\"\n        if self.default_factory is None:\n            raise ValueError(\"Cannot add to a DefaultDict with no default_factory\")\n        self.set(name, self.default_factory())  # pylint: disable=E1102\n        return 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):\n    r\"\"\"\n    Add a new default factory to the dictionary.\n\n    Args:\n        name:\n\n    Raises:\n        ValueError: If `default_factory` is None.\n\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    \"\"\"\n    if self.default_factory is None:\n        raise ValueError(\"Cannot add to a DefaultDict with no default_factory\")\n    self.set(name, self.default_factory())  # pylint: disable=E1102\n    return 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):\n    r\"\"\"\n    `FlatDict` with attribute-style access.\n\n    `FlatDict` inherits from built-in `dict`.\n\n    It comes with many easy to use helper methods, such as `merge`, `sort`, `difference`, `intersect`.\n\n    It also has full support for IO operations, such as `json` and `yaml`.\n\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\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\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\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\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\n    Attributes:\n        indent: Indentation level in printing and dumping to json or yaml.\n\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\n        Although it is possible to override other internal methods, it is not recommended to do so.\n\n        `__class__`, `__dict__`, and `getattr` are reserved and cannot be overrode in any manner.\n\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\n    # pylint: disable=R0904\n\n    indent: int = 2\n\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        if len(args) == 1:\n            arg = args[0]\n            if isinstance(arg, (PathLike, str, bytes)):\n                arg = self.load(arg)\n            elif isinstance(arg, (Namespace,)):\n                arg = vars(arg)\n            args = (arg,)\n        super().__init__(*args, **kwargs)\n\n    def __post_init__(self, *args, **kwargs) -> None:\n        pass\n\n    def __getattribute__(self, name: Any) -> Any:\n        if (name not in (\"getattr\",) and not (name.startswith(\"__\") and name.endswith(\"__\"))) and name in self:\n            if name in dir(self.__class__):\n                value = super().__getattribute__(name)\n                if isinstance(value, (property, staticmethod, classmethod)) or callable(value):\n                    return value\n            return self.get(name)\n        return super().__getattribute__(name)\n\n    def get(self, name: Any, default: Any = None) -> Any:\n        r\"\"\"\n        Get value from `FlatDict`.\n\n        Args:\n            name:\n            default:\n\n        Returns:\n            value:\n                If `FlatDict` does not contain `name`, return `default`.\n\n        Raises:\n            KeyError: If `FlatDict` does not contain `name` and `default` is not specified.\n            TypeError: If `name` is not hashable.\n\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        \"\"\"\n\n        if name in self:\n            return dict.__getitem__(self, name)\n        if default is not Null:\n            return default\n        return self.__missing__(name)\n\n    def __getitem__(self, name: Any) -> Any:\n        return self.get(name, default=Null)\n\n    def __getattr__(self, name: Any) -> Any:\n        try:\n            return self.get(name, default=Null)\n        except KeyError:\n            raise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\n\n    def set(self, name: Any, value: Any) -> None:\n        r\"\"\"\n        Set value of `FlatDict`.\n\n        Args:\n            name:\n            value:\n\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        \"\"\"\n\n        if name is Null:\n            raise ValueError(\"name must not be null\")\n        if name in self and isinstance(self.get(name), Variable):\n            self.get(name).set(value)\n        else:\n            dict.__setitem__(self, name, value)\n\n    def __setitem__(self, name: Any, value: Any) -> None:\n        self.set(name, value)\n\n    def __setattr__(self, name: Any, value: Any) -> None:\n        self.set(name, value)\n\n    def delete(self, name: Any) -> None:\n        r\"\"\"\n        Delete value from `FlatDict`.\n\n        Args:\n            name:\n\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        \"\"\"\n\n        dict.__delitem__(self, name)\n\n    def __delitem__(self, name: Any) -> None:\n        return self.delete(name)\n\n    def __delattr__(self, name: Any) -> None:\n        try:\n            self.delete(name)\n        except KeyError:\n            raise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\n\n    def __missing__(self, name: Any) -> Any:  # pylint: disable=R1710\n        raise KeyError(name)\n\n    def validate(self) -> None:\n        r\"\"\"\n        Validate `FlatDict`.\n\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\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        \"\"\"\n\n        self._validate(self)\n\n    @staticmethod\n    def _validate(obj) -> None:\n        if isinstance(obj, FlatDict):\n            annotations = get_annotations(obj)\n            for name, value in obj.items():\n                if annotations and name in annotations and not isvalid(value, annotations[name]):\n                    raise TypeError(f\"'{name}' has invalid type. Value {value} is not of type {annotations[name]}.\")\n                if isinstance(value, Variable):\n                    try:\n                        value.validate()\n                    except TypeError as exc:\n                        raise TypeError(f\"'{name}' has invalid type. {exc}\") from None\n                    except ValueError as exc:\n                        raise ValueError(f\"'{name}' has invalid value. {exc}\") from None\n\n    def getattr(self, name: str, default: Any = Null) -> Any:\n        r\"\"\"\n        Get attribute of `FlatDict`.\n\n        Note that it won't retrieve value in `FlatDict`,\n\n        Args:\n            name:\n            default:\n\n        Returns:\n            value: If `FlatDict` does not contain `name`, return `default`.\n\n        Raises:\n            AttributeError: If `FlatDict` does not contain `name` and `default` is not specified.\n\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        \"\"\"\n\n        try:\n            if name in self.__dict__:\n                return self.__dict__[name]\n            if name in self.__class__.__dict__:\n                return self.__class__.__dict__[name]\n            return super().getattr(name, default)  # type: ignore[misc]\n        except AttributeError:\n            if default is not Null:\n                return default\n            raise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\n\n    def setattr(self, name: str, value: Any) -> None:\n        r\"\"\"\n        Set attribute of `FlatDict`.\n\n        Note that it won't alter values in `FlatDict`.\n\n        Args:\n            name:\n            value:\n\n        Warns:\n            RuntimeWarning: If name already exists in `FlatDict`.\n\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        \"\"\"\n\n        if name in self:\n            warn(\n                f\"{name} already exists in {self.__class__.__name__}.\\n\"\n                f\"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.\",\n                RuntimeWarning,\n            )\n        self.__dict__[name] = value\n\n    def delattr(self, name: str) -> None:\n        r\"\"\"\n        Delete attribute of `FlatDict`.\n\n        Note that it won't delete values in `FlatDict`.\n\n        Args:\n            name:\n\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        \"\"\"\n\n        del self.__dict__[name]\n\n    def hasattr(self, name: str) -> bool:\n        r\"\"\"\n        Determine if an attribute exists in `FlatDict`.\n\n        Args:\n            name:\n\n        Returns:\n            (bool):\n\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        \"\"\"\n\n        try:\n            if name in self.__dict__ or name in self.__class__.__dict__:\n                return True\n            return super().hasattr(name)  # type: ignore[misc]\n        except AttributeError:\n            return False\n\n    def dict(self, flatten: bool = False) -> Mapping | Sequence | Set:\n        r\"\"\"\n        Convert `FlatDict` to other `Mapping`.\n\n        Args:\n            flatten: Whether to flatten [`NestedDict`][chanfig.NestedDict].\n\n        Returns:\n            (Mapping):\n\n        See Also:\n            [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`.\n\n        **Alias**:\n\n        + `to_dict`\n\n        Examples:\n            >>> d = FlatDict(a=1, b=2, c=3)\n            >>> d.dict()\n            {'a': 1, 'b': 2, 'c': 3}\n        \"\"\"\n\n        return to_dict(self, flatten)\n\n    def to_dict(self, flatten: bool = False) -> Mapping | Sequence | Set:\n        r\"\"\"\n        Alias of [`dict`][chanfig.FlatDict.dict].\n        \"\"\"\n\n        return self.dict(flatten)\n\n    @classmethod\n    def from_dict(cls, obj: Mapping | Sequence) -> Any:  # pylint: disable=R0911\n        r\"\"\"\n        Convert `Mapping` or `Sequence` to `FlatDict`.\n\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        \"\"\"\n\n        if obj is None:\n            return cls()\n        if issubclass(cls, FlatDict):\n            cls = cls.empty  # type: ignore[assignment] # pylint: disable=W0642\n        if isinstance(obj, Mapping):\n            return cls(obj)\n        if isinstance(obj, Sequence):\n            try:\n                return cls(obj)\n            except ValueError:\n                return [cls(json) for json in obj]\n        raise TypeError(f\"Expected Mapping or Sequence, but got {type(obj)}.\")\n\n    def sort(self, key: Callable | None = None, reverse: bool = False) -> Self:\n        r\"\"\"\n        Sort `FlatDict`.\n\n        Returns:\n            (FlatDict):\n\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        \"\"\"\n\n        items = sorted(self.items(), key=key, reverse=reverse)\n        self.clear()\n        for k, v in items:  # pylint: disable=C0103\n            self[k] = v\n        return self\n\n    def interpolate(  # pylint: disable=R0912\n        self, use_variable: bool = True, interpolators: MutableMapping | None = None, unsafe_eval: bool = False\n    ) -> Self:\n        r\"\"\"\n        Perform Variable interpolation.\n\n        Variable interpolation allows you to set the value of one key to be the value of another key easily.\n\n        Args:\n            use_variable: Whether to convert values to `Variable` objects.\n            interpolators: Mapping contains values for interpolation. Defaults to `self`.\n            unsafe_eval: Whether to evaluate interpolated values.\n\n        Raises:\n            ValueError: If value is not interpolatable.\n            ValueError: If reference to itself.\n            ValueError: If has circular reference.\n\n        See Also:\n            [Variable][`chanfig.Variable`]: Mutable wrapper of immutable objects.\n\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(unsafe_eval=True).dict()\n            {'a': 1, 'b': 1, 'c': 1.1}\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        \"\"\"\n        # pylint: disable=C0103\n\n        interpolators = interpolators or self\n        placeholders: dict[str, list[str]] = {}\n        for key, value in self.all_items():\n            if isinstance(value, list):\n                for v in value:\n                    self.find_placeholders(key, v, placeholders)\n            elif isinstance(value, Mapping):\n                for v in value.values():\n                    self.find_placeholders(key, v, placeholders)\n            else:\n                self.find_placeholders(key, value, placeholders)\n        circular_references = find_circular_reference(placeholders)\n        if circular_references:\n            raise ValueError(f\"Circular reference found: {'->'.join(circular_references)}.\")\n        if use_variable:\n            placeholder_names = {i for j in placeholders.values() for i in j}\n            for name in list(placeholder_names.difference(placeholders.keys())):\n                if name not in interpolators:\n                    raise ValueError(f\"{name} is not found in {interpolators}.\")\n                if not isinstance(interpolators[name], Variable):\n                    interpolators[name] = Variable(interpolators[name])\n        for key, value in placeholders.items():\n            if isinstance(self[key], list):\n                for index, v in enumerate(self[key]):\n                    self[key][index] = self.substitute(v, interpolators, value)\n            elif isinstance(self[key], Mapping):\n                for k, v in self[key].items():\n                    self[key][k] = self.substitute(v, interpolators, value)\n            else:\n                self[key] = self.substitute(self[key], interpolators, value)\n            if unsafe_eval and isinstance(self[key], str):\n                with suppress(SyntaxError):\n                    self[key] = eval(self[key])  # pylint: disable=W0123\n        return self\n\n    @staticmethod\n    def find_placeholders(key, value, placeholders):\n        placeholder = find_placeholders(value)\n        if placeholder:\n            for index, name in enumerate(placeholder):\n                if name.startswith(\".\"):\n                    placeholder[index] = key.rsplit(\".\", 1)[0] + name\n                if key == name:\n                    raise ValueError(f\"Cannot interpolate {key} to itself.\")\n            placeholders[key] = placeholder\n\n    @staticmethod\n    def substitute(placeholder, interpolators, value):\n        try:\n            if len(value) == 1 and placeholder.startswith(\"${\") and placeholder.endswith(\"}\"):\n                return interpolators[value[0]]\n            return placeholder.replace(\"$\", \"\").format(**interpolators)\n        except KeyError as exc:\n            raise ValueError(f\"{exc} is not found in {interpolators}.\") from None\n\n    def merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> Self:\n        r\"\"\"\n        Merge `other` into `FlatDict`.\n\n        Args:\n            *args: `Mapping` or `Sequence` to be merged.\n            overwrite: Whether to overwrite existing values.\n            **kwargs: `Mapping` to be merged.\n\n        Returns:\n            self:\n\n        **Alias**:\n\n        + `union`\n\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        \"\"\"\n\n        if len(args) == 1:\n            args = args[0]\n            if isinstance(args, (PathLike, str, bytes)):\n                args = self.load(args)  # type: ignore[assignment]\n                warn(\n                    \"merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.\",\n                    PendingDeprecationWarning,\n                )\n            self._merge(self, args, overwrite=overwrite)\n        elif len(args) > 1:\n            self._merge(self, args, overwrite=overwrite)\n        if kwargs:\n            self._merge(self, kwargs, overwrite=overwrite)\n        return self\n\n    @staticmethod\n    def _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping:\n        if not that:\n            return this\n        if isinstance(that, Mapping):\n            that = that.items()\n        for key, value in that:\n            if key in this and isinstance(this[key], Mapping):\n                if isinstance(value, Mapping):\n                    FlatDict._merge(this[key], value)\n                elif overwrite:\n                    if isinstance(value, FlatDict):\n                        this.set(key, value)\n                    else:\n                        this[key] = value\n            elif overwrite or key not in this:\n                this.set(key, value)\n        return this\n\n    def union(self, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Alias of [`merge`][chanfig.FlatDict.merge].\n        \"\"\"\n        return self.merge(*args, **kwargs)\n\n    def merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Merge content of `file` into `FlatDict`.\n\n        Args:\n            file (File):\n            *args: Passed to [`load`][chanfig.FlatDict.load].\n            **kwargs: Passed to [`load`][chanfig.FlatDict.load].\n\n        Returns:\n            self:\n\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        \"\"\"\n\n        return self.merge(self.load(file, *args, **kwargs))\n\n    def intersect(self, other: Mapping | Iterable | PathStr) -> Self:\n        r\"\"\"\n        Intersection of `FlatDict` and `other`.\n\n        Args:\n            other (Mapping | Iterable | PathStr):\n\n        Returns:\n            (FlatDict):\n\n        **Alias**:\n\n        + `inter`\n\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        \"\"\"\n\n        if isinstance(other, (PathLike, str, bytes)):\n            other = self.load(other)\n        if isinstance(other, (Mapping,)):\n            other = self.empty(other).items()\n        if not isinstance(other, Iterable):\n            raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n        return self.empty(**{key: value for key, value in other if key in self and self[key] == value})  # type: ignore\n\n    def inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Alias of [`intersect`][chanfig.FlatDict.intersect].\n        \"\"\"\n        return self.intersect(other, *args, **kwargs)\n\n    def difference(self, other: Mapping | Iterable | PathStr) -> Self:\n        r\"\"\"\n        Difference between `FlatDict` and `other`.\n\n        Args:\n            other:\n\n        Returns:\n            (FlatDict):\n\n        **Alias**:\n\n        + `diff`\n\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        \"\"\"\n\n        if isinstance(other, (PathLike, str, bytes)):\n            other = self.load(other)\n        if isinstance(other, (Mapping,)):\n            other = self.empty(other).items()\n        if not isinstance(other, Iterable):\n            raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n        return self.empty(\n            **{key: value for key, value in other if key not in self or self[key] != value}  # type: ignore[misc]\n        )\n\n    def diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Alias of [`difference`][chanfig.FlatDict.difference].\n        \"\"\"\n        return self.difference(other, *args, **kwargs)\n\n    def to(self, cls: str | TorchDevice | TorchDType) -> Self:  # pragma: no cover\n        r\"\"\"\n        Convert values of `FlatDict` to target `cls`.\n\n        Args:\n            cls (str | torch.device | torch.dtype):\n\n        Returns:\n            self:\n\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\n        # pylint: disable=C0103\n\n        if isinstance(cls, (str, TorchDevice, TorchDType)):\n            for k, v in self.all_items():\n                if hasattr(v, \"to\"):\n                    self[k] = v.to(cls)\n            return self\n\n        raise TypeError(f\"to() only support torch.dtype and torch.device, but got {cls}.\")\n\n    def cpu(self) -> Self:  # pragma: no cover\n        r\"\"\"\n        Move all tensors to cpu.\n\n        Returns:\n            self:\n\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        \"\"\"\n\n        return self.to(TorchDevice(\"cpu\"))\n\n    def gpu(self) -> Self:  # pragma: no cover\n        r\"\"\"\n        Move all tensors to gpu.\n\n        Returns:\n            self:\n\n        **Alias**:\n\n        + `cuda`\n\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        \"\"\"\n\n        return self.to(TorchDevice(\"cuda\"))\n\n    def cuda(self) -> Self:  # pragma: no cover\n        r\"\"\"\n        Alias of [`gpu`][chanfig.FlatDict.gpu].\n        \"\"\"\n        return self.gpu()\n\n    def tpu(self) -> Self:  # pragma: no cover\n        r\"\"\"\n        Move all tensors to tpu.\n\n        Returns:\n            self:\n\n        **Alias**:\n\n        + `xla`\n\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        \"\"\"\n\n        return self.to(TorchDevice(\"xla\"))\n\n    def xla(self) -> Self:  # pragma: no cover\n        r\"\"\"\n        Alias of [`tpu`][chanfig.FlatDict.tpu].\n        \"\"\"\n        return self.tpu()\n\n    def copy(self) -> Self:\n        r\"\"\"\n        Create a shallow copy of `FlatDict`.\n\n        Returns:\n            (FlatDict):\n\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        \"\"\"\n\n        return copy(self)\n\n    def __deepcopy__(self, memo: Mapping | None = None) -> Self:\n        # pylint: disable=C0103\n\n        if memo is not None and id(self) in memo:\n            return memo[id(self)]\n        ret = self.empty()\n        ret.__dict__.update(deepcopy(self.__dict__))\n        for k, v in self.items():\n            if isinstance(v, FlatDict):\n                ret[k] = v.deepcopy(memo=memo)\n            else:\n                ret[k] = deepcopy(v)\n        return ret\n\n    def deepcopy(self, memo: Mapping | None = None) -> Self:  # pylint: disable=W0613\n        r\"\"\"\n        Create a deep copy of `FlatDict`.\n\n        Returns:\n            (FlatDict):\n\n        **Alias**:\n\n        + `clone`\n\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        \"\"\"\n\n        return deepcopy(self)\n\n    def clone(self, memo: Mapping | None = None) -> Self:\n        r\"\"\"\n        Alias of [`deepcopy`][chanfig.FlatDict.deepcopy].\n        \"\"\"\n        return self.deepcopy(memo=memo)\n\n    def save(  # pylint: disable=W1113\n        self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n    ) -> None:\n        r\"\"\"\n        Save `FlatDict` to file.\n\n        Raises:\n            ValueError: If save to `IO` and `method` is not specified.\n            TypeError: If save to unsupported extension.\n\n        **Alias**:\n\n        + `save`\n\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        \"\"\"\n\n        if method is None:\n            if isinstance(file, (IOBase, IO)):\n                raise ValueError(\"`method` must be specified when saving to IO.\")\n            method = splitext(file)[-1][1:]\n        extension = method.lower()\n        if extension in YAML:\n            return self.yaml(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026\n        if extension in JSON:\n            return self.json(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026\n        raise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\n\n    def dump(  # pylint: disable=W1113\n        self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n    ) -> None:\n        r\"\"\"\n        Alias of [`save`][chanfig.FlatDict.save].\n        \"\"\"\n        return self.save(file, method, *args, **kwargs)\n\n    @classmethod\n    def load(  # pylint: disable=W1113\n        cls, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n    ) -> Self:\n        \"\"\"\n        Load `FlatDict` from file.\n\n        Args:\n            file: File to load from.\n            method: File type, should be in `JSON` or `YAML`.\n\n        Returns:\n            (FlatDict):\n\n        Raises:\n            ValueError: If load from `IO` and `method` is not specified.\n            TypeError: If dump to unsupported extension.\n\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        \"\"\"\n\n        if method is None:\n            if isinstance(file, (IOBase, IO)):\n                raise ValueError(\"`method` must be specified when loading from IO.\")\n            method = splitext(file)[-1][1:]\n        extension = method.lower()\n        if extension in JSON:\n            return cls.from_json(file, *args, **kwargs)\n        if extension in YAML:\n            return cls.from_yaml(file, *args, **kwargs)\n        raise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\n\n    def json(self, file: File, *args: Any, **kwargs: Any) -> None:\n        r\"\"\"\n        Dump `FlatDict` to json file.\n\n        This method internally calls `self.jsons()` to generate json string.\n        You may overwrite `jsons` in case something is not json serializable.\n\n        Examples:\n            >>> d = FlatDict(a=1, b=2, c=3)\n            >>> d.json(\"tests/test.json\")\n        \"\"\"\n\n        with self.open(file, mode=\"w\") as fp:  # pylint: disable=C0103\n            fp.write(self.jsons(*args, **kwargs))\n\n    @classmethod\n    def from_json(cls, file: File, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Construct `FlatDict` from json file.\n\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\n        Returns:\n            (FlatDict):\n\n        Examples:\n            >>> d = FlatDict.from_json('tests/test.json')\n            >>> d.dict()\n            {'a': 1, 'b': 2, 'c': 3}\n        \"\"\"\n\n        with cls.open(file) as fp:  # pylint: disable=C0103\n            if isinstance(file, (IOBase, IO)):\n                return cls.from_jsons(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]\n            return cls.from_jsons(fp.read(), *args, **kwargs)\n\n    def jsons(self, *args: Any, **kwargs: Any) -> str:\n        r\"\"\"\n        Dump `FlatDict` to json string.\n\n        Returns:\n            (str):\n\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        \"\"\"\n\n        kwargs.setdefault(\"cls\", JsonEncoder)\n        kwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\n        return json_dumps(self.dict(), *args, **kwargs)\n\n    @classmethod\n    def from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Construct `FlatDict` from json string.\n\n        Returns:\n            (FlatDict):\n\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        \"\"\"\n\n        return cls.from_dict(json_loads(string, *args, **kwargs))\n\n    def yaml(self, file: File, *args: Any, **kwargs: Any) -> None:\n        r\"\"\"\n        Dump `FlatDict` to yaml file.\n\n        This method internally calls `self.yamls()` to generate yaml string.\n        You may overwrite `yamls` in case something is not yaml serializable.\n\n        Examples:\n            >>> d = FlatDict(a=1, b=2, c=3)\n            >>> d.yaml(\"tests/test.yaml\")\n        \"\"\"\n\n        with self.open(file, mode=\"w\") as fp:  # pylint: disable=C0103\n            self.yamls(fp, *args, **kwargs)\n\n    @classmethod\n    def from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Construct `FlatDict` from yaml file.\n\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\n        Returns:\n            (FlatDict):\n\n        Examples:\n            >>> FlatDict.from_yaml('tests/test.yaml').dict()\n            {'a': 1, 'b': 2, 'c': 3}\n        \"\"\"\n\n        kwargs.setdefault(\"Loader\", YamlLoader)\n        with cls.open(file) as fp:  # pylint: disable=C0103\n            if isinstance(file, (IOBase, IO)):\n                return cls.from_yamls(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]\n            return cls.from_dict(yaml_load(fp, *args, **kwargs))\n\n    def yamls(self, *args: Any, **kwargs: Any) -> str:\n        r\"\"\"\n        Dump `FlatDict` to yaml string.\n\n        Returns:\n            (str):\n\n        Examples:\n            >>> FlatDict(a=1, b=2, c=3).yamls()\n            'a: 1\\nb: 2\\nc: 3\\n'\n        \"\"\"\n\n        kwargs.setdefault(\"Dumper\", YamlDumper)\n        kwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\n        return yaml_dump(self.dict(), *args, **kwargs)\n\n    @classmethod\n    def from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Construct `FlatDict` from yaml string.\n\n        Returns:\n            (FlatDict):\n\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        \"\"\"\n\n        kwargs.setdefault(\"Loader\", SafeLoader)\n        return cls.from_dict(yaml_load(string, *args, **kwargs))\n\n    @staticmethod\n    @contextmanager\n    def open(file: File, *args: Any, encoding: str = \"utf-8\", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]:\n        r\"\"\"\n        Open file IO from file path or IO.\n\n        This methods extends the ability of built-in `open` by allowing it to accept an `IOBase` object.\n\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\n        Yields:\n            (Generator[IOBase | IO, Any, Any]):\n\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        \"\"\"\n\n        if isinstance(file, (IOBase, IO)):\n            yield file\n        elif isinstance(file, (PathLike, str, bytes)):\n            try:\n                file = open(file, *args, encoding=encoding, **kwargs)  # type: ignore[call-overload] # noqa: SIM115\n                yield file  # type: ignore[misc]\n            finally:\n                with suppress(Exception):\n                    file.close()  # type: ignore[union-attr]\n        else:\n            raise TypeError(f\"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}\")\n\n    @classmethod\n    def empty(cls, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Initialise an empty `FlatDict`.\n\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\n        This method will preserve everything in `FlatDict.__class__.__dict__`.\n\n        Returns:\n            (FlatDict):\n\n        See Also:\n            [`empty_like`][chanfig.FlatDict.empty_like]\n\n        Examples:\n            >>> d = FlatDict(a=[])\n            >>> c = d.empty()\n            >>> c.dict()\n            {}\n        \"\"\"\n\n        empty = cls.__new__(cls)\n        empty.merge(*args, **kwargs)  # pylint: disable=W0212\n        return empty\n\n    def empty_like(self, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Initialise an empty copy of `FlatDict`.\n\n        This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`.\n\n        For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this\n        method.\n\n        Returns:\n            (FlatDict):\n\n        See Also:\n            [`empty`][chanfig.FlatDict.empty]\n\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        \"\"\"\n\n        empty = self.empty(*args, **kwargs)\n        empty.__dict__.update(self.__dict__)\n        return empty\n\n    def all_keys(self) -> Generator:\n        r\"\"\"\n        Equivalent to `keys`.\n\n        This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n\n        See Also:\n            [`all_keys`][chanfig.NestedDict.all_keys]\n        \"\"\"\n        yield from self.keys()\n\n    def all_values(self) -> Generator:\n        r\"\"\"\n        Equivalent to `keys`.\n\n        This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n\n        See Also:\n            [`all_values`][chanfig.NestedDict.all_values]\n        \"\"\"\n        yield from self.values()\n\n    def all_items(self) -> Generator:\n        r\"\"\"\n        Equivalent to `keys`.\n\n        This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n\n        See Also:\n            [`all_items`][chanfig.NestedDict.all_items]\n        \"\"\"\n        yield from self.items()\n\n    def dropnull(self) -> Self:\n        r\"\"\"\n        Drop key-value pairs with `Null` value.\n\n        Returns:\n            (FlatDict):\n\n        **Alias**:\n\n        + `dropna`\n\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        \"\"\"\n\n        return self.empty({k: v for k, v in self.all_items() if v is not Null})\n\n    def dropna(self) -> Self:\n        r\"\"\"\n        Alias of [`dropnull`][chanfig.FlatDict.dropnull].\n        \"\"\"\n        return self.dropnull()\n\n    @staticmethod\n    def extra_repr() -> str:  # pylint: disable=C0116\n        return \"\"\n\n    def __repr__(self) -> str:\n        extra_lines = []\n        extra_repr = self.extra_repr()\n        # empty string will be split into list ['']\n        if extra_repr:\n            extra_lines = extra_repr.split(\"\\n\")\n        child_lines = []\n        for key, value in self.items():\n            key_repr = repr(key)\n            value_repr = repr(value)\n            value_repr = self._add_indent(value_repr)\n            child_lines.append(f\"({key_repr}): {value_repr}\")\n            # child_lines.append(f\"{key_repr}: {value_repr}\")\n        lines = extra_lines + child_lines\n\n        main_repr = self.__class__.__name__ + \"(\"\n        if lines:\n            # simple one-liner info, which most builtin Modules will use\n            if len(extra_lines) == 1 and not child_lines:\n                main_repr += extra_lines[0]\n            elif len(child_lines) == 1 and not extra_lines and len(child_lines[0]) < 10:\n                main_repr += child_lines[0]\n            else:\n                main_repr += \"\\n  \" + \"\\n  \".join(lines) + \"\\n\"\n\n        main_repr += \")\"\n        return main_repr\n\n    def _add_indent(self, text: str) -> str:\n        lines = text.split(\"\\n\")\n        # don't do anything for single-line stuff\n        if len(lines) == 1:\n            return text\n        first = lines.pop(0)\n        lines = [(self.getattr(\"indent\", 2) * \" \") + line for line in lines]\n        text = \"\\n\".join(lines)\n        text = first + \"\\n\" + text\n        return text\n\n    def __format__(self, format_spec: str) -> str:\n        return repr(self.empty({k: v.__format__(format_spec) for k, v in self.all_items()}))\n\n    def __hash__(self):\n        return hash(frozenset(self.items()))\n\n    def _ipython_display_(self):  # pragma: no cover\n        return repr(self)\n\n    def _ipython_canary_method_should_not_exist_(self):  # pragma: no cover\n        return None\n\n    def aihwerij235234ljsdnp34ksodfipwoe234234jlskjdf(self):  # pragma: no cover\n        return None\n\n    def __rich__(self):  # pragma: no cover\n        return 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:\n    r\"\"\"\n    Equivalent to `keys`.\n\n    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n\n    See Also:\n        [`all_items`][chanfig.NestedDict.all_items]\n    \"\"\"\n    yield 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:\n    r\"\"\"\n    Equivalent to `keys`.\n\n    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n\n    See Also:\n        [`all_keys`][chanfig.NestedDict.all_keys]\n    \"\"\"\n    yield 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:\n    r\"\"\"\n    Equivalent to `keys`.\n\n    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n\n    See Also:\n        [`all_values`][chanfig.NestedDict.all_values]\n    \"\"\"\n    yield 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) -> Self:\n    r\"\"\"\n    Alias of [`deepcopy`][chanfig.FlatDict.deepcopy].\n    \"\"\"\n    return 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) -> Self:\n    r\"\"\"\n    Create a shallow copy of `FlatDict`.\n\n    Returns:\n        (FlatDict):\n\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    \"\"\"\n\n    return copy(self)\n
"},{"location":"flat_dict/#chanfig.FlatDict.cpu","title":"cpu()","text":"

Move all tensors to cpu.

Returns:

Name Type Description self Self

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) -> Self:  # pragma: no cover\n    r\"\"\"\n    Move all tensors to cpu.\n\n    Returns:\n        self:\n\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    \"\"\"\n\n    return 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) -> Self:  # pragma: no cover\n    r\"\"\"\n    Alias of [`gpu`][chanfig.FlatDict.gpu].\n    \"\"\"\n    return 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:

  • clone

Examples:

Python Console Session
>>> d = FlatDict(a=[])\n>>> d.setattr(\"name\", \"Chang\")\n>>> c = d.deepcopy()\n>>> c.dict()\n{'a': []}\n>>> d.a.append(1)\n>>> c.dict()\n{'a': []}\n>>> c.getattr(\"name\")\n'Chang'\n>>> d == d.clone()  # alias\nTrue\n
Source code in chanfig/flat_dict.py Python
def deepcopy(self, memo: Mapping | None = None) -> Self:  # pylint: disable=W0613\n    r\"\"\"\n    Create a deep copy of `FlatDict`.\n\n    Returns:\n        (FlatDict):\n\n    **Alias**:\n\n    + `clone`\n\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    \"\"\"\n\n    return 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:\n    r\"\"\"\n    Delete attribute of `FlatDict`.\n\n    Note that it won't delete values in `FlatDict`.\n\n    Args:\n        name:\n\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    \"\"\"\n\n    del 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:\n    r\"\"\"\n    Delete value from `FlatDict`.\n\n    Args:\n        name:\n\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    \"\"\"\n\n    dict.__delitem__(self, name)\n
"},{"location":"flat_dict/#chanfig.FlatDict.dict","title":"dict(flatten=False)","text":"

Convert FlatDict to other Mapping.

Parameters:

Name Type Description Default flatten bool

Whether to flatten NestedDict.

False

Returns:

Type Description Mapping See Also

to_dict: Implementation of dict.

Alias:

  • to_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, flatten: bool = False) -> Mapping | Sequence | Set:\n    r\"\"\"\n    Convert `FlatDict` to other `Mapping`.\n\n    Args:\n        flatten: Whether to flatten [`NestedDict`][chanfig.NestedDict].\n\n    Returns:\n        (Mapping):\n\n    See Also:\n        [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`.\n\n    **Alias**:\n\n    + `to_dict`\n\n    Examples:\n        >>> d = FlatDict(a=1, b=2, c=3)\n        >>> d.dict()\n        {'a': 1, 'b': 2, 'c': 3}\n    \"\"\"\n\n    return to_dict(self, flatten)\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) -> Self:\n    r\"\"\"\n    Alias of [`difference`][chanfig.FlatDict.difference].\n    \"\"\"\n    return 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:

  • diff

Examples:

Python Console Session
>>> d = FlatDict(a=1, b=2, c=3)\n>>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d.difference(n).dict()\n{'b': 'b', 'c': 'c', 'd': 'd'}\n>>> l = [('c', 3), ('d', 4)]\n>>> d.difference(l).dict()\n{'d': 4}\n>>> d.merge(l).difference(\"tests/test.yaml\").dict()\n{}\n>>> d.difference(1)\nTraceback (most recent call last):\nTypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n>>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict()  # alias\n{'b': 'b', 'c': 'c', 'd': 'd'}\n
Source code in chanfig/flat_dict.py Python
def difference(self, other: Mapping | Iterable | PathStr) -> Self:\n    r\"\"\"\n    Difference between `FlatDict` and `other`.\n\n    Args:\n        other:\n\n    Returns:\n        (FlatDict):\n\n    **Alias**:\n\n    + `diff`\n\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    \"\"\"\n\n    if isinstance(other, (PathLike, str, bytes)):\n        other = self.load(other)\n    if isinstance(other, (Mapping,)):\n        other = self.empty(other).items()\n    if not isinstance(other, Iterable):\n        raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n    return self.empty(\n        **{key: value for key, value in other if key not in self or self[key] != value}  # type: ignore[misc]\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) -> Self:\n    r\"\"\"\n    Alias of [`dropnull`][chanfig.FlatDict.dropnull].\n    \"\"\"\n    return self.dropnull()\n
"},{"location":"flat_dict/#chanfig.FlatDict.dropnull","title":"dropnull()","text":"

Drop key-value pairs with Null value.

Returns:

Type Description FlatDict

Alias:

  • dropna

Examples:

Python Console Session
>>> d = FlatDict(a=Null, b=Null, c=3)\n>>> d.dict()\n{'a': Null, 'b': Null, 'c': 3}\n>>> d.dropnull().dict()\n{'c': 3}\n>>> d.dropna().dict()  # alias\n{'c': 3}\n
Source code in chanfig/flat_dict.py Python
def dropnull(self) -> Self:\n    r\"\"\"\n    Drop key-value pairs with `Null` value.\n\n    Returns:\n        (FlatDict):\n\n    **Alias**:\n\n    + `dropna`\n\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    \"\"\"\n\n    return 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(  # pylint: disable=W1113\n    self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n) -> None:\n    r\"\"\"\n    Alias of [`save`][chanfig.FlatDict.save].\n    \"\"\"\n    return 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) -> Self:\n    r\"\"\"\n    Initialise an empty `FlatDict`.\n\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\n    This method will preserve everything in `FlatDict.__class__.__dict__`.\n\n    Returns:\n        (FlatDict):\n\n    See Also:\n        [`empty_like`][chanfig.FlatDict.empty_like]\n\n    Examples:\n        >>> d = FlatDict(a=[])\n        >>> c = d.empty()\n        >>> c.dict()\n        {}\n    \"\"\"\n\n    empty = cls.__new__(cls)\n    empty.merge(*args, **kwargs)  # pylint: disable=W0212\n    return 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) -> Self:\n    r\"\"\"\n    Initialise an empty copy of `FlatDict`.\n\n    This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`.\n\n    For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this\n    method.\n\n    Returns:\n        (FlatDict):\n\n    See Also:\n        [`empty`][chanfig.FlatDict.empty]\n\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    \"\"\"\n\n    empty = self.empty(*args, **kwargs)\n    empty.__dict__.update(self.__dict__)\n    return 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\n    r\"\"\"\n    Convert `Mapping` or `Sequence` to `FlatDict`.\n\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    \"\"\"\n\n    if obj is None:\n        return cls()\n    if issubclass(cls, FlatDict):\n        cls = cls.empty  # type: ignore[assignment] # pylint: disable=W0642\n    if isinstance(obj, Mapping):\n        return cls(obj)\n    if isinstance(obj, Sequence):\n        try:\n            return cls(obj)\n        except ValueError:\n            return [cls(json) for json in obj]\n    raise 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) -> Self:\n    r\"\"\"\n    Construct `FlatDict` from json file.\n\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\n    Returns:\n        (FlatDict):\n\n    Examples:\n        >>> d = FlatDict.from_json('tests/test.json')\n        >>> d.dict()\n        {'a': 1, 'b': 2, 'c': 3}\n    \"\"\"\n\n    with cls.open(file) as fp:  # pylint: disable=C0103\n        if isinstance(file, (IOBase, IO)):\n            return cls.from_jsons(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]\n        return 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) -> Self:\n    r\"\"\"\n    Construct `FlatDict` from json string.\n\n    Returns:\n        (FlatDict):\n\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    \"\"\"\n\n    return 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) -> Self:\n    r\"\"\"\n    Construct `FlatDict` from yaml file.\n\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\n    Returns:\n        (FlatDict):\n\n    Examples:\n        >>> FlatDict.from_yaml('tests/test.yaml').dict()\n        {'a': 1, 'b': 2, 'c': 3}\n    \"\"\"\n\n    kwargs.setdefault(\"Loader\", YamlLoader)\n    with cls.open(file) as fp:  # pylint: disable=C0103\n        if isinstance(file, (IOBase, IO)):\n            return cls.from_yamls(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]\n        return 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) -> Self:\n    r\"\"\"\n    Construct `FlatDict` from yaml string.\n\n    Returns:\n        (FlatDict):\n\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    \"\"\"\n\n    kwargs.setdefault(\"Loader\", SafeLoader)\n    return 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:\n    r\"\"\"\n    Get value from `FlatDict`.\n\n    Args:\n        name:\n        default:\n\n    Returns:\n        value:\n            If `FlatDict` does not contain `name`, return `default`.\n\n    Raises:\n        KeyError: If `FlatDict` does not contain `name` and `default` is not specified.\n        TypeError: If `name` is not hashable.\n\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    \"\"\"\n\n    if name in self:\n        return dict.__getitem__(self, name)\n    if default is not Null:\n        return default\n    return 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:\n    r\"\"\"\n    Get attribute of `FlatDict`.\n\n    Note that it won't retrieve value in `FlatDict`,\n\n    Args:\n        name:\n        default:\n\n    Returns:\n        value: If `FlatDict` does not contain `name`, return `default`.\n\n    Raises:\n        AttributeError: If `FlatDict` does not contain `name` and `default` is not specified.\n\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    \"\"\"\n\n    try:\n        if name in self.__dict__:\n            return self.__dict__[name]\n        if name in self.__class__.__dict__:\n            return self.__class__.__dict__[name]\n        return super().getattr(name, default)  # type: ignore[misc]\n    except AttributeError:\n        if default is not Null:\n            return default\n        raise 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 Self

Alias:

  • cuda

Examples:

Python Console Session
>>> import torch\n>>> d = FlatDict(a=torch.tensor(1))\n>>> d.gpu().dict()\n{'a': tensor(1, device='cuda:0')}\n>>> d.cuda().dict()  # alias\n{'a': tensor(1, device='cuda:0')}\n
Source code in chanfig/flat_dict.py Python
def gpu(self) -> Self:  # pragma: no cover\n    r\"\"\"\n    Move all tensors to gpu.\n\n    Returns:\n        self:\n\n    **Alias**:\n\n    + `cuda`\n\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    \"\"\"\n\n    return 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:\n    r\"\"\"\n    Determine if an attribute exists in `FlatDict`.\n\n    Args:\n        name:\n\n    Returns:\n        (bool):\n\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    \"\"\"\n\n    try:\n        if name in self.__dict__ or name in self.__class__.__dict__:\n            return True\n        return super().hasattr(name)  # type: ignore[misc]\n    except AttributeError:\n        return 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) -> Self:\n    r\"\"\"\n    Alias of [`intersect`][chanfig.FlatDict.intersect].\n    \"\"\"\n    return self.intersect(other, *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.interpolate","title":"interpolate(use_variable=True, interpolators=None, unsafe_eval=False)","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 unsafe_eval bool

Whether to evaluate interpolated values.

False

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(unsafe_eval=True).dict()\n{'a': 1, 'b': 1, 'c': 1.1}\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)\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(  # pylint: disable=R0912\n    self, use_variable: bool = True, interpolators: MutableMapping | None = None, unsafe_eval: bool = False\n) -> Self:\n    r\"\"\"\n    Perform Variable interpolation.\n\n    Variable interpolation allows you to set the value of one key to be the value of another key easily.\n\n    Args:\n        use_variable: Whether to convert values to `Variable` objects.\n        interpolators: Mapping contains values for interpolation. Defaults to `self`.\n        unsafe_eval: Whether to evaluate interpolated values.\n\n    Raises:\n        ValueError: If value is not interpolatable.\n        ValueError: If reference to itself.\n        ValueError: If has circular reference.\n\n    See Also:\n        [Variable][`chanfig.Variable`]: Mutable wrapper of immutable objects.\n\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(unsafe_eval=True).dict()\n        {'a': 1, 'b': 1, 'c': 1.1}\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    \"\"\"\n    # pylint: disable=C0103\n\n    interpolators = interpolators or self\n    placeholders: dict[str, list[str]] = {}\n    for key, value in self.all_items():\n        if isinstance(value, list):\n            for v in value:\n                self.find_placeholders(key, v, placeholders)\n        elif isinstance(value, Mapping):\n            for v in value.values():\n                self.find_placeholders(key, v, placeholders)\n        else:\n            self.find_placeholders(key, value, placeholders)\n    circular_references = find_circular_reference(placeholders)\n    if circular_references:\n        raise ValueError(f\"Circular reference found: {'->'.join(circular_references)}.\")\n    if use_variable:\n        placeholder_names = {i for j in placeholders.values() for i in j}\n        for name in list(placeholder_names.difference(placeholders.keys())):\n            if name not in interpolators:\n                raise ValueError(f\"{name} is not found in {interpolators}.\")\n            if not isinstance(interpolators[name], Variable):\n                interpolators[name] = Variable(interpolators[name])\n    for key, value in placeholders.items():\n        if isinstance(self[key], list):\n            for index, v in enumerate(self[key]):\n                self[key][index] = self.substitute(v, interpolators, value)\n        elif isinstance(self[key], Mapping):\n            for k, v in self[key].items():\n                self[key][k] = self.substitute(v, interpolators, value)\n        else:\n            self[key] = self.substitute(self[key], interpolators, value)\n        if unsafe_eval and isinstance(self[key], str):\n            with suppress(SyntaxError):\n                self[key] = eval(self[key])  # pylint: disable=W0123\n    return 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:

  • inter

Examples:

Python Console Session
>>> d = FlatDict(a=1, b=2, c=3)\n>>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d.intersect(n).dict()\n{}\n>>> l = [('c', 3), ('d', 4)]\n>>> d.intersect(l).dict()\n{'c': 3}\n>>> d.merge(l).intersect(\"tests/test.yaml\").dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> d.intersect(1)\nTraceback (most recent call last):\nTypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n>>> d.inter(FlatDict(b='b', c='c', d='d')).dict()  # alias\n{}\n
Source code in chanfig/flat_dict.py Python
def intersect(self, other: Mapping | Iterable | PathStr) -> Self:\n    r\"\"\"\n    Intersection of `FlatDict` and `other`.\n\n    Args:\n        other (Mapping | Iterable | PathStr):\n\n    Returns:\n        (FlatDict):\n\n    **Alias**:\n\n    + `inter`\n\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    \"\"\"\n\n    if isinstance(other, (PathLike, str, bytes)):\n        other = self.load(other)\n    if isinstance(other, (Mapping,)):\n        other = self.empty(other).items()\n    if not isinstance(other, Iterable):\n        raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n    return 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:\n    r\"\"\"\n    Dump `FlatDict` to json file.\n\n    This method internally calls `self.jsons()` to generate json string.\n    You may overwrite `jsons` in case something is not json serializable.\n\n    Examples:\n        >>> d = FlatDict(a=1, b=2, c=3)\n        >>> d.json(\"tests/test.json\")\n    \"\"\"\n\n    with self.open(file, mode=\"w\") as fp:  # pylint: disable=C0103\n        fp.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:\n    r\"\"\"\n    Dump `FlatDict` to json string.\n\n    Returns:\n        (str):\n\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    \"\"\"\n\n    kwargs.setdefault(\"cls\", JsonEncoder)\n    kwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\n    return 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

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\n    cls, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n) -> Self:\n    \"\"\"\n    Load `FlatDict` from file.\n\n    Args:\n        file: File to load from.\n        method: File type, should be in `JSON` or `YAML`.\n\n    Returns:\n        (FlatDict):\n\n    Raises:\n        ValueError: If load from `IO` and `method` is not specified.\n        TypeError: If dump to unsupported extension.\n\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    \"\"\"\n\n    if method is None:\n        if isinstance(file, (IOBase, IO)):\n            raise ValueError(\"`method` must be specified when loading from IO.\")\n        method = splitext(file)[-1][1:]\n    extension = method.lower()\n    if extension in JSON:\n        return cls.from_json(file, *args, **kwargs)\n    if extension in YAML:\n        return cls.from_yaml(file, *args, **kwargs)\n    raise 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 Self

Alias:

  • union

Examples:

Python Console Session
>>> d = FlatDict(a=1, b=2, c=3)\n>>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d.merge(n).dict()\n{'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n>>> l = [('c', 3), ('d', 4)]\n>>> d.merge(l).dict()\n{'a': 1, 'b': 'b', 'c': 3, 'd': 4}\n>>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict()  # alias\n{'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d = FlatDict()\n>>> d.merge({1: 1, 2: 2, 3:3}).dict()\n{1: 1, 2: 2, 3: 3}\n>>> d.merge(d.clone()).dict()\n{1: 1, 2: 2, 3: 3}\n>>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict()\n{1: 1, 2: 2, 3: 3, 4: 4, 5: 5}\n
Source code in chanfig/flat_dict.py Python
def merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> Self:\n    r\"\"\"\n    Merge `other` into `FlatDict`.\n\n    Args:\n        *args: `Mapping` or `Sequence` to be merged.\n        overwrite: Whether to overwrite existing values.\n        **kwargs: `Mapping` to be merged.\n\n    Returns:\n        self:\n\n    **Alias**:\n\n    + `union`\n\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    \"\"\"\n\n    if len(args) == 1:\n        args = args[0]\n        if isinstance(args, (PathLike, str, bytes)):\n            args = self.load(args)  # type: ignore[assignment]\n            warn(\n                \"merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.\",\n                PendingDeprecationWarning,\n            )\n        self._merge(self, args, overwrite=overwrite)\n    elif len(args) > 1:\n        self._merge(self, args, overwrite=overwrite)\n    if kwargs:\n        self._merge(self, kwargs, overwrite=overwrite)\n    return 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 Self

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) -> Self:\n    r\"\"\"\n    Merge content of `file` into `FlatDict`.\n\n    Args:\n        file (File):\n        *args: Passed to [`load`][chanfig.FlatDict.load].\n        **kwargs: Passed to [`load`][chanfig.FlatDict.load].\n\n    Returns:\n        self:\n\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    \"\"\"\n\n    return 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\n>>> io = open(\"tests/test.yaml\")\n>>> with FlatDict.open(io) as fp:\n...     print(fp.read())\na: 1\nb: 2\nc: 3\n\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]:\n    r\"\"\"\n    Open file IO from file path or IO.\n\n    This methods extends the ability of built-in `open` by allowing it to accept an `IOBase` object.\n\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\n    Yields:\n        (Generator[IOBase | IO, Any, Any]):\n\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    \"\"\"\n\n    if isinstance(file, (IOBase, IO)):\n        yield file\n    elif isinstance(file, (PathLike, str, bytes)):\n        try:\n            file = open(file, *args, encoding=encoding, **kwargs)  # type: ignore[call-overload] # noqa: SIM115\n            yield file  # type: ignore[misc]\n        finally:\n            with suppress(Exception):\n                file.close()  # type: ignore[union-attr]\n    else:\n        raise 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:

  • save

Examples:

Python Console Session
>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.save(\"tests/test.yaml\")\n>>> d.save(\"test.conf\")\nTraceback (most recent call last):\nTypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n>>> with open(\"test.yaml\", \"w\") as f:\n...     d.save(f)\nTraceback (most recent call last):\nValueError: `method` must be specified when saving to IO.\n
Source code in chanfig/flat_dict.py Python
def save(  # pylint: disable=W1113\n    self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n) -> None:\n    r\"\"\"\n    Save `FlatDict` to file.\n\n    Raises:\n        ValueError: If save to `IO` and `method` is not specified.\n        TypeError: If save to unsupported extension.\n\n    **Alias**:\n\n    + `save`\n\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    \"\"\"\n\n    if method is None:\n        if isinstance(file, (IOBase, IO)):\n            raise ValueError(\"`method` must be specified when saving to IO.\")\n        method = splitext(file)[-1][1:]\n    extension = method.lower()\n    if extension in YAML:\n        return self.yaml(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026\n    if extension in JSON:\n        return self.json(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026\n    raise 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:\n    r\"\"\"\n    Set value of `FlatDict`.\n\n    Args:\n        name:\n        value:\n\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    \"\"\"\n\n    if name is Null:\n        raise ValueError(\"name must not be null\")\n    if name in self and isinstance(self.get(name), Variable):\n        self.get(name).set(value)\n    else:\n        dict.__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:\n    r\"\"\"\n    Set attribute of `FlatDict`.\n\n    Note that it won't alter values in `FlatDict`.\n\n    Args:\n        name:\n        value:\n\n    Warns:\n        RuntimeWarning: If name already exists in `FlatDict`.\n\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    \"\"\"\n\n    if name in self:\n        warn(\n            f\"{name} already exists in {self.__class__.__name__}.\\n\"\n            f\"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.\",\n            RuntimeWarning,\n        )\n    self.__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) -> Self:\n    r\"\"\"\n    Sort `FlatDict`.\n\n    Returns:\n        (FlatDict):\n\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    \"\"\"\n\n    items = sorted(self.items(), key=key, reverse=reverse)\n    self.clear()\n    for k, v in items:  # pylint: disable=C0103\n        self[k] = v\n    return 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 Self

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) -> Self:  # pragma: no cover\n    r\"\"\"\n    Convert values of `FlatDict` to target `cls`.\n\n    Args:\n        cls (str | torch.device | torch.dtype):\n\n    Returns:\n        self:\n\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\n    # pylint: disable=C0103\n\n    if isinstance(cls, (str, TorchDevice, TorchDType)):\n        for k, v in self.all_items():\n            if hasattr(v, \"to\"):\n                self[k] = v.to(cls)\n        return self\n\n    raise TypeError(f\"to() only support torch.dtype and torch.device, but got {cls}.\")\n
"},{"location":"flat_dict/#chanfig.FlatDict.to_dict","title":"to_dict(flatten=False)","text":"

Alias of dict.

Source code in chanfig/flat_dict.py Python
def to_dict(self, flatten: bool = False) -> Mapping | Sequence | Set:\n    r\"\"\"\n    Alias of [`dict`][chanfig.FlatDict.dict].\n    \"\"\"\n\n    return self.dict(flatten)\n
"},{"location":"flat_dict/#chanfig.FlatDict.tpu","title":"tpu()","text":"

Move all tensors to tpu.

Returns:

Name Type Description self Self

Alias:

  • xla

Examples:

Python Console Session
>>> import torch\n>>> d = FlatDict(a=torch.tensor(1))\n>>> d.tpu().dict()\n{'a': tensor(1, device='xla:0')}\n>>> d.xla().dict()  # alias\n{'a': tensor(1, device='xla:0')}\n
Source code in chanfig/flat_dict.py Python
def tpu(self) -> Self:  # pragma: no cover\n    r\"\"\"\n    Move all tensors to tpu.\n\n    Returns:\n        self:\n\n    **Alias**:\n\n    + `xla`\n\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    \"\"\"\n\n    return 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) -> Self:\n    r\"\"\"\n    Alias of [`merge`][chanfig.FlatDict.merge].\n    \"\"\"\n    return 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:\n    r\"\"\"\n    Validate `FlatDict`.\n\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\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    \"\"\"\n\n    self._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) -> Self:  # pragma: no cover\n    r\"\"\"\n    Alias of [`tpu`][chanfig.FlatDict.tpu].\n    \"\"\"\n    return 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:\n    r\"\"\"\n    Dump `FlatDict` to yaml file.\n\n    This method internally calls `self.yamls()` to generate yaml string.\n    You may overwrite `yamls` in case something is not yaml serializable.\n\n    Examples:\n        >>> d = FlatDict(a=1, b=2, c=3)\n        >>> d.yaml(\"tests/test.yaml\")\n    \"\"\"\n\n    with self.open(file, mode=\"w\") as fp:  # pylint: disable=C0103\n        self.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:\n    r\"\"\"\n    Dump `FlatDict` to yaml string.\n\n    Returns:\n        (str):\n\n    Examples:\n        >>> FlatDict(a=1, b=2, c=3).yamls()\n        'a: 1\\nb: 2\\nc: 3\\n'\n    \"\"\"\n\n    kwargs.setdefault(\"Dumper\", YamlDumper)\n    kwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\n    return yaml_dump(self.dict(), *args, **kwargs)\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 | Sequence | Set

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, flatten: bool = False) -> Mapping | Sequence | Set:\n    r\"\"\"\n    Convert an object to a dict.\n\n    Note that when converting a `set` object, it may be converted to a `tuple` object if its values is not hashable.\n\n    Args:\n        obj: Object to be converted.\n\n    Returns:\n        A dict.\n\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    \"\"\"\n\n    if flatten and isinstance(obj, FlatDict):\n        return {k: to_dict(v) for k, v in obj.all_items()}\n    if isinstance(obj, Mapping):\n        return {k: to_dict(v) for k, v in obj.items()}\n    if isinstance(obj, list):\n        return [to_dict(v) for v in obj]\n    if isinstance(obj, tuple):\n        return tuple(to_dict(v) for v in obj)\n    if isinstance(obj, set):\n        try:\n            return {to_dict(v) for v in obj}\n        except TypeError:\n            return tuple(to_dict(v) for v in obj)\n    if isinstance(obj, Variable):\n        return obj.value\n    if is_dataclass(obj):\n        return asdict(obj)\n    if hasattr(obj, \"to_dict\"):\n        return obj.to_dict()\n    return 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:

  • save

Examples:

Python Console Session
>>> obj = {\"a\": 1, \"b\": 2, \"c\": 3}\n>>> save(obj, \"test.yaml\")\n>>> save(obj, \"test.json\")\n>>> save(obj, \"test.conf\")\nTraceback (most recent call last):\nTypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n>>> with open(\"test.yaml\", \"w\") as f:\n...     save(obj, f)\nTraceback (most recent call last):\nValueError: `method` must be specified when saving to IO.\n
Source code in chanfig/functional.py Python
def save(  # pylint: disable=W1113\n    obj, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n) -> None:\n    r\"\"\"\n    Save `FlatDict` to file.\n\n    Raises:\n        ValueError: If save to `IO` and `method` is not specified.\n        TypeError: If save to unsupported extension.\n\n    **Alias**:\n\n    + `save`\n\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    \"\"\"\n\n    if isinstance(obj, FlatDict):\n        obj.save(file, method, *args, **kwargs)\n        return\n\n    data = to_dict(obj)\n    if method is None:\n        if isinstance(file, IOBase):\n            raise ValueError(\"`method` must be specified when saving to IO.\")\n        method = splitext(file)[-1][1:]\n    extension = method.lower()\n    if extension in YAML:\n        with FlatDict.open(file, mode=\"w\") as fp:  # pylint: disable=C0103\n            yaml_dump(data, fp, *args, **kwargs)\n        return\n    if extension in JSON:\n        with FlatDict.open(file, mode=\"w\") as fp:  # pylint: disable=C0103\n            fp.write(json_dumps(data, *args, **kwargs))\n        return\n    raise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\n

options: heading_level: 0

Load a file into a FlatDict.

This function simply calls cls.load, by default, cls is NestedDict.

Parameters:

Name Type Description Default file PathStr

The file to load.

required cls type[FlatDict]

The class of the file to load. Defaults to NestedDict.

NestedDict *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(  # pylint: disable=W1113\n    file: PathStr, cls: type[FlatDict] = NestedDict, *args: Any, **kwargs: Any\n) -> FlatDict:\n    r\"\"\"\n    Load a file into a `FlatDict`.\n\n    This function simply calls `cls.load`, by default, `cls` is `NestedDict`.\n\n    Args:\n        file: The file to load.\n        cls: The class of the file to load. Defaults to `NestedDict`.\n        *args: The arguments to pass to `NestedDict.load`.\n        **kwargs: The keyword arguments to pass to `NestedDict.load`.\n\n    See Also:\n        [`load`][chanfig.FlatDict.load]\n\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    \"\"\"\n\n    return cls.load(file, *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 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:\n    r\"\"\"\n    Apply `func` to all children of `obj`.\n\n    Note that this method is meant for non-in-place modification of `obj` and should return the original object.\n\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\n    Returns:\n        (Any): Return value of `func`.\n\n    See Also:\n        [`apply_`][chanfig.nested_dict.apply_]: Apply an in-place operation.\n    \"\"\"\n\n    if isinstance(obj, NestedDict):\n        return obj.empty_like(**{k: apply(v, func, *args, **kwargs) for k, v in obj.items()})\n    if isinstance(obj, Mapping):\n        return {k: apply(v, func, *args, **kwargs) for k, v in obj.items()}\n    if isinstance(obj, list):\n        return [apply(v, func, *args, **kwargs) for v in obj]\n    if isinstance(obj, tuple):\n        return tuple(apply(v, func, *args, **kwargs) for v in obj)\n    if isinstance(obj, set):\n        try:\n            return {apply(v, func, *args, **kwargs) for v in obj}\n        except TypeError:\n            tuple(apply(v, func, *args, **kwargs) for v in obj)\n    return 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:\n    r\"\"\"\n    Apply `func` to all children of `obj`.\n\n    Note that this method is meant for non-in-place modification of `obj` and should return a new object.\n\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\n    Returns:\n        (Any): Return value of `func`.\n\n    See Also:\n        [`apply_`][chanfig.nested_dict.apply]: Apply a non-in-place operation.\n    \"\"\"\n    # pylint: disable=C0103\n\n    if isinstance(obj, Mapping):\n        for v in obj.values():\n            apply_(v, func, *args, **kwargs)\n    if isinstance(obj, (list, tuple, set)):\n        for v in obj:\n            apply_(v, func, *args, **kwargs)\n    return 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>>> NestedDict({\"i.d\": [{'c': 1013}, {'k': 1031}]})\nNestedDict(\n  ('i'): NestedDict(\n    ('d'): [NestedDict(\n      ('c'): 1013\n    ), NestedDict(\n      ('k'): 1031\n    )]\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):  # pylint: disable=E1136\n    r\"\"\"\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\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\n    This behaviour allows you to pass keyword arguments to other functions as easy as `func1(**d.func1)`.\n\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\n    `NestedDict` also introduce `all_keys`, `all_values`, `all_items` methods to get all keys, values, items\n    respectively in the nested structure.\n\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\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\n        `convert_mapping` is automatically applied to arguments during initialisation.\n\n    Examples:\n        >>> NestedDict({\"f.n\": \"chang\"})\n        NestedDict(\n          ('f'): NestedDict(\n            ('n'): 'chang'\n          )\n        )\n        >>> NestedDict({\"i.d\": [{'c': 1013}, {'k': 1031}]})\n        NestedDict(\n          ('i'): NestedDict(\n            ('d'): [NestedDict(\n              ('c'): 1013\n            ), NestedDict(\n              ('k'): 1031\n            )]\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    \"\"\"\n\n    convert_mapping: bool = False\n    delimiter: str = \".\"\n    fallback: bool = False\n\n    def __init__(\n        self,\n        *args: Any,\n        default_factory: Callable | None = None,\n        convert_mapping: bool | None = None,\n        fallback: bool | None = None,\n        **kwargs: Any,\n    ) -> None:\n        super().__init__(default_factory)\n        self.merge(*args, **kwargs)\n        if convert_mapping is not None:\n            self.setattr(\"convert_mapping\", convert_mapping)\n        if fallback is not None:\n            self.setattr(\"fallback\", fallback)\n\n    def all_keys(self) -> Generator:\n        r\"\"\"\n        Get all keys of `NestedDict`.\n\n        Returns:\n            (Generator):\n\n        Examples:\n            >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n            >>> list(d.all_keys())\n            ['a', 'b.c', 'b.d']\n        \"\"\"\n\n        delimiter = self.getattr(\"delimiter\", \".\")\n\n        @wraps(self.all_keys)\n        def all_keys(self, prefix=Null):\n            for key, value in self.items():\n                if prefix is not Null:\n                    key = str(prefix) + str(delimiter) + str(key)\n                if isinstance(value, NestedDict):\n                    yield from all_keys(value, key)\n                else:\n                    yield key\n\n        return all_keys(self)\n\n    def all_values(self) -> Generator:\n        r\"\"\"\n        Get all values of `NestedDict`.\n\n        Returns:\n            (Generator):\n\n        Examples:\n            >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n            >>> list(d.all_values())\n            [1, 2, 3]\n        \"\"\"\n\n        for value in self.values():\n            if isinstance(value, NestedDict):\n                yield from value.all_values()\n            else:\n                yield value\n\n    def all_items(self) -> Generator:\n        r\"\"\"\n        Get all items of `NestedDict`.\n\n        Returns:\n            (Generator):\n\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        \"\"\"\n\n        delimiter = self.getattr(\"delimiter\", \".\")\n\n        @wraps(self.all_items)\n        def all_items(self, prefix=Null):\n            for key, value in self.items():\n                if prefix is not Null:\n                    key = str(prefix) + str(delimiter) + str(key)\n                if isinstance(value, NestedDict):\n                    yield from all_items(value, key)\n                else:\n                    yield key, value\n\n        return all_items(self)\n\n    def apply(self, func: Callable, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Recursively apply a function to `NestedDict` and its children.\n\n        Note:\n            This method is meant for non-in-place modification of `obj`, for example, [`to`][chanfig.NestedDict.to].\n\n        Args:\n            func (Callable):\n\n        See Also:\n            [`apply_`][chanfig.NestedDict.apply_]: Apply an in-place operation.\n            [`apply`][chanfig.nested_dict.apply]: Implementation of `apply`.\n\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        \"\"\"\n\n        return apply(self, func, *args, **kwargs)\n\n    def apply_(self, func: Callable, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Recursively apply a function to `NestedDict` and its children.\n\n        Note:\n            This method is meant for in-place modification of `obj`, for example, [`freeze`][chanfig.Config.freeze].\n\n        Args:\n            func (Callable):\n\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\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        \"\"\"\n\n        apply_(self, func, *args, **kwargs)\n        return self\n\n    def get(self, name: Any, default: Any = None, fallback: bool | None = None) -> Any:\n        r\"\"\"\n        Get value from `NestedDict`.\n\n        Note that `default` has higher priority than `default_factory`.\n\n        Args:\n            name:\n            default:\n\n        Returns:\n            value:\n                If `NestedDict` does not contain `name`, return `default`.\n                If `default` is not specified, return `default_factory()`.\n\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\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        \"\"\"\n\n        delimiter = self.getattr(\"delimiter\", \".\")\n        if fallback is None:\n            fallback = self.getattr(\"fallback\", False)\n        fallback_name = name.split(delimiter)[-1] if isinstance(name, str) else name\n        fallback_value = Null\n        try:\n            while isinstance(name, str) and delimiter in name:\n                if fallback and fallback_name in self:\n                    fallback_value = self.get(fallback_name)\n                name, rest = name.split(delimiter, 1)\n                self, name = self[name], rest  # pylint: disable=W0642\n        except (KeyError, AttributeError, TypeError):\n            if fallback and fallback_value is not Null:\n                return fallback_value\n            if default is not Null:\n                return default\n            raise KeyError(name) from None\n        if (fallback and fallback_value is not Null) and (not isinstance(self, Iterable) or name not in self):\n            return fallback_value\n        # if value is a python dict\n        if not isinstance(self, NestedDict):\n            if name not in self and default is not Null:\n                return default\n            return self[name]\n        return super().get(name, default)\n\n    def set(  # pylint: disable=W0221\n        self,\n        name: Any,\n        value: Any,\n        convert_mapping: bool | None = None,\n    ) -> None:\n        r\"\"\"\n        Set value of `NestedDict`.\n\n        Args:\n            name:\n            value:\n            convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n                Defaults to self.convert_mapping.\n\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\n\n        full_name = name\n        delimiter = self.getattr(\"delimiter\", \".\")\n        if convert_mapping is None:\n            convert_mapping = self.getattr(\"convert_mapping\", False)\n        default_factory = self.getattr(\"default_factory\", self.empty)\n        try:\n            while isinstance(name, str) and delimiter in name:\n                name, rest = name.split(delimiter, 1)\n                if name in dir(self) and isinstance(getattr(self.__class__, name), (property, cached_property)):\n                    self, name = getattr(self, name), rest\n                elif name not in self and isinstance(self, Mapping):\n                    default = (\n                        self.__missing__(name, default_factory()) if hasattr(self, \"__missing__\") else default_factory()\n                    )\n                    self, name = default, rest\n                else:\n                    self, name = self[name], rest\n                if isinstance(self, NestedDict):\n                    default_factory = self.getattr(\"default_factory\", self.empty)\n        except (AttributeError, TypeError):\n            raise KeyError(name) from None\n\n        if (\n            convert_mapping\n            and not isinstance(value, default_factory if isinstance(default_factory, type) else type(self))\n            and not isinstance(value, Variable)\n        ):\n            if isinstance(value, Mapping):\n                try:\n                    value = default_factory(**value)\n                except TypeError:\n                    value = default_factory(value)\n            if isinstance(value, list):\n                value = [default_factory(v) if isinstance(v, Mapping) else v for v in value]\n            if isinstance(value, tuple):\n                value = tuple(default_factory(v) if isinstance(v, Mapping) else v for v in value)\n            if isinstance(value, set):\n                value = {default_factory(v) if isinstance(v, Mapping) else v for v in list(value)}\n        if isinstance(self, NestedDict):\n            super().set(name, value)\n        elif isinstance(self, Mapping):\n            dict.__setitem__(self, name, value)\n        else:\n            raise ValueError(\n                f\"Cannot set `{full_name}` to `{value}`, as `{delimiter.join(full_name.split(delimiter)[:-1])}={self}`.\"\n            )\n\n    def delete(self, name: Any) -> None:\n        r\"\"\"\n        Delete value from `NestedDict`.\n\n        Args:\n            name:\n\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        \"\"\"\n\n        delimiter = self.getattr(\"delimiter\", \".\")\n        try:\n            while isinstance(name, str) and delimiter in name:\n                name, rest = name.split(delimiter, 1)\n                self, name = self[name], rest  # pylint: disable=W0642\n        except (AttributeError, TypeError):\n            raise KeyError(name) from None\n        # if value is a python dict\n        if not isinstance(self, NestedDict):\n            del self[name]\n            return\n        super().delete(name)\n\n    def pop(self, name: Any, default: Any = Null) -> Any:\n        r\"\"\"\n        Pop value from `NestedDict`.\n\n        Args:\n            name:\n            default:\n\n        Returns:\n            value: If `NestedDict` does not contain `name`, return `default`.\n\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        \"\"\"\n\n        delimiter = self.getattr(\"delimiter\", \".\")\n        try:\n            while isinstance(name, str) and delimiter in name:\n                name, rest = name.split(delimiter, 1)\n                self, name = self[name], rest  # pylint: disable=W0642\n        except (AttributeError, TypeError):\n            raise KeyError(name) from None\n        if not isinstance(self, dict) or name not in self:\n            if default is not Null:\n                return default\n            raise KeyError(name)\n        return super().pop(name)\n\n    def setdefault(  # type: ignore[override]  # pylint: disable=R0912,W0221\n        self,\n        name: Any,\n        value: Any,\n        convert_mapping: bool | None = None,\n    ) -> Any:\n        r\"\"\"\n        Set default value for `NestedDict`.\n\n        Args:\n            name:\n            value:\n            convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n                Defaults to self.convert_mapping.\n\n        Returns:\n            value: If `NestedDict` does not contain `name`, return `value`.\n\n        Examples:\n            >>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\", \"n.a.b.c\": 1})\n            >>> d.setdefault(\"d.i\", 1031)\n            1031\n            >>> d.setdefault(\"i.d\", \"chang\")\n            1013\n            >>> d.setdefault(\"f.n\", 1013)\n            'chang'\n            >>> d.setdefault(\"n.a.b.d\", 2)\n            2\n        \"\"\"\n        # pylint: disable=W0642\n\n        full_name = name\n        delimiter = self.getattr(\"delimiter\", \".\")\n        if convert_mapping is None:\n            convert_mapping = self.getattr(\"convert_mapping\", False)\n        default_factory = self.getattr(\"default_factory\", self.empty)\n        try:\n            while isinstance(name, str) and delimiter in name:\n                name, rest = name.split(delimiter, 1)\n                if name in dir(self) and isinstance(getattr(self.__class__, name), (property, cached_property)):\n                    self, name = getattr(self, name), rest\n                elif name not in self and isinstance(self, Mapping):\n                    default = (\n                        self.__missing__(name, default_factory()) if hasattr(self, \"__missing__\") else default_factory()\n                    )\n                    self, name = default, rest\n                else:\n                    self, name = self[name], rest\n                if isinstance(self, NestedDict):\n                    default_factory = self.getattr(\"default_factory\", self.empty)\n        except (AttributeError, TypeError):\n            raise KeyError(name) from None\n\n        if isinstance(self, NestedDict) and name in self:\n            return super().get(name)\n        elif isinstance(self, Mapping) and name in self:\n            dict.__getitem__(self, name)\n\n        if (\n            convert_mapping\n            and isinstance(value, Mapping)\n            and not isinstance(value, default_factory if isinstance(default_factory, type) else type(self))\n            and not isinstance(value, Variable)\n        ):\n            try:\n                value = default_factory(**value)\n            except TypeError:\n                value = default_factory(value)\n        if isinstance(self, NestedDict):\n            super().set(name, value)\n        elif isinstance(self, Mapping):\n            dict.__setitem__(self, name, value)\n        else:\n            raise ValueError(\n                f\"Cannot set `{full_name}` to `{value}`, as `{delimiter.join(full_name.split(delimiter)[:-1])}={self}`.\"\n            )\n        return value\n\n    def validate(self) -> None:\n        r\"\"\"\n        Validate `NestedDict`.\n\n        Raises:\n            TypeError: If `Variable` has invalid type.\n            ValueError: If `Variable` has invalid value.\n\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        \"\"\"\n\n        self.apply_(self._validate)\n\n    def sort(self, key: Callable | None = None, reverse: bool = False, recursive: bool = True) -> Self:\n        r\"\"\"\n        Sort `NestedDict`.\n\n        Args:\n            recursive (bool): Whether to apply `sort` recursively.\n\n        Returns:\n            (NestedDict):\n\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]\n        \"\"\"\n\n        if recursive:\n            for value in self.values():\n                if isinstance(value, FlatDict):\n                    value.sort(key=key, reverse=reverse)\n        return super().sort(key=key, reverse=reverse)\n\n    @staticmethod\n    def _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping:\n        if not that:\n            return this\n        if isinstance(that, Mapping):\n            that = that.items()\n        with this.converting() if isinstance(this, NestedDict) else nullcontext():\n            for key, value in that:\n                if key in this and isinstance(this[key], Mapping):\n                    if isinstance(value, Mapping):\n                        NestedDict._merge(this[key], value, overwrite)\n                    elif overwrite:\n                        if isinstance(this, NestedDict):\n                            this.set(key, value)\n                        else:\n                            this[key] = value\n                elif key in dir(this) and isinstance(getattr(this.__class__, key, None), (property, cached_property)):\n                    if isinstance(getattr(this, key, None), FlatDict):\n                        getattr(this, key).merge(value, overwrite=overwrite)\n                    else:\n                        setattr(this, key, value)\n                elif overwrite or key not in this:\n                    if isinstance(this, NestedDict):\n                        this.set(key, value)\n                    else:\n                        this[key] = value\n        return this\n\n    def intersect(self, other: Mapping | Iterable | PathStr, recursive: bool = True) -> Self:  # pylint: disable=W0221\n        r\"\"\"\n        Intersection of `NestedDict` and `other`.\n\n        Args:\n            other (Mapping | Iterable | PathStr):\n            recursive (bool):\n\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        \"\"\"\n\n        if isinstance(other, (PathLike, str, bytes)):\n            other = self.load(other)\n        if isinstance(other, (Mapping,)):\n            other = self.empty(other).items()\n        if not isinstance(other, Iterable):\n            raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n        return self.empty(self._intersect(self, other, recursive))\n\n    @staticmethod\n    def _intersect(this: NestedDict, that: Iterable, recursive: bool = True) -> Mapping:\n        ret: NestedDict = NestedDict()\n        for key, value in that:\n            if key in this:\n                if isinstance(this[key], NestedDict) and isinstance(value, Mapping) and recursive:\n                    intersects = this[key].intersect(value)\n                    if intersects:\n                        ret[key] = intersects\n                elif this[key] == value:\n                    ret[key] = value\n        return ret\n\n    def difference(  # pylint: disable=W0221, C0103\n        self, other: Mapping | Iterable | PathStr, recursive: bool = True\n    ) -> Self:\n        r\"\"\"\n        Difference between `NestedDict` and `other`.\n\n        Args:\n            other (Mapping | Iterable | PathStr):\n            recursive (bool):\n\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        \"\"\"\n\n        if isinstance(other, (PathLike, str, bytes)):\n            other = self.load(other)\n        if isinstance(other, (Mapping,)):\n            other = self.empty(other).items()\n        if not isinstance(other, Iterable):\n            raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n        return self.empty(self._difference(self, other, recursive))\n\n    @staticmethod\n    def _difference(this: NestedDict, that: Iterable, recursive: bool = True) -> Mapping:\n        ret: NestedDict = NestedDict()\n        for key, value in that:\n            if key not in this:\n                ret[key] = value\n            elif isinstance(this[key], NestedDict) and isinstance(value, Mapping) and recursive:\n                differences = this[key].difference(value)\n                if differences:\n                    ret[key] = differences\n            elif this[key] != value:\n                ret[key] = value\n        return ret\n\n    @contextmanager\n    def converting(self):\n        convert_mapping = self.getattr(\"convert_mapping\", False)\n        try:\n            self.setattr(\"convert_mapping\", True)\n            yield\n        finally:\n            self.setattr(\"convert_mapping\", convert_mapping)\n\n    def __contains__(self, name: Any) -> bool:\n        delimiter = self.getattr(\"delimiter\", \".\")\n        try:\n            while isinstance(name, str) and delimiter in name:\n                name, rest = name.split(delimiter, 1)\n                if super().__contains__(name):\n                    self, name = self[name], rest  # pylint: disable=W0642\n                else:\n                    return False\n            return super().__contains__(name)\n        except (TypeError, KeyError):  # TypeError when name is not in self\n            return 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:\n    r\"\"\"\n    Get all items of `NestedDict`.\n\n    Returns:\n        (Generator):\n\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    \"\"\"\n\n    delimiter = self.getattr(\"delimiter\", \".\")\n\n    @wraps(self.all_items)\n    def all_items(self, prefix=Null):\n        for key, value in self.items():\n            if prefix is not Null:\n                key = str(prefix) + str(delimiter) + str(key)\n            if isinstance(value, NestedDict):\n                yield from all_items(value, key)\n            else:\n                yield key, value\n\n    return 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:\n    r\"\"\"\n    Get all keys of `NestedDict`.\n\n    Returns:\n        (Generator):\n\n    Examples:\n        >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n        >>> list(d.all_keys())\n        ['a', 'b.c', 'b.d']\n    \"\"\"\n\n    delimiter = self.getattr(\"delimiter\", \".\")\n\n    @wraps(self.all_keys)\n    def all_keys(self, prefix=Null):\n        for key, value in self.items():\n            if prefix is not Null:\n                key = str(prefix) + str(delimiter) + str(key)\n            if isinstance(value, NestedDict):\n                yield from all_keys(value, key)\n            else:\n                yield key\n\n    return 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:\n    r\"\"\"\n    Get all values of `NestedDict`.\n\n    Returns:\n        (Generator):\n\n    Examples:\n        >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n        >>> list(d.all_values())\n        [1, 2, 3]\n    \"\"\"\n\n    for value in self.values():\n        if isinstance(value, NestedDict):\n            yield from value.all_values()\n        else:\n            yield 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) -> Self:\n    r\"\"\"\n    Recursively apply a function to `NestedDict` and its children.\n\n    Note:\n        This method is meant for non-in-place modification of `obj`, for example, [`to`][chanfig.NestedDict.to].\n\n    Args:\n        func (Callable):\n\n    See Also:\n        [`apply_`][chanfig.NestedDict.apply_]: Apply an in-place operation.\n        [`apply`][chanfig.nested_dict.apply]: Implementation of `apply`.\n\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    \"\"\"\n\n    return 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) -> Self:\n    r\"\"\"\n    Recursively apply a function to `NestedDict` and its children.\n\n    Note:\n        This method is meant for in-place modification of `obj`, for example, [`freeze`][chanfig.Config.freeze].\n\n    Args:\n        func (Callable):\n\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\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    \"\"\"\n\n    apply_(self, func, *args, **kwargs)\n    return 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:\n    r\"\"\"\n    Delete value from `NestedDict`.\n\n    Args:\n        name:\n\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    \"\"\"\n\n    delimiter = self.getattr(\"delimiter\", \".\")\n    try:\n        while isinstance(name, str) and delimiter in name:\n            name, rest = name.split(delimiter, 1)\n            self, name = self[name], rest  # pylint: disable=W0642\n    except (AttributeError, TypeError):\n        raise KeyError(name) from None\n    # if value is a python dict\n    if not isinstance(self, NestedDict):\n        del self[name]\n        return\n    super().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\n    self, other: Mapping | Iterable | PathStr, recursive: bool = True\n) -> Self:\n    r\"\"\"\n    Difference between `NestedDict` and `other`.\n\n    Args:\n        other (Mapping | Iterable | PathStr):\n        recursive (bool):\n\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    \"\"\"\n\n    if isinstance(other, (PathLike, str, bytes)):\n        other = self.load(other)\n    if isinstance(other, (Mapping,)):\n        other = self.empty(other).items()\n    if not isinstance(other, Iterable):\n        raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n    return self.empty(self._difference(self, other, recursive))\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:\n    r\"\"\"\n    Get value from `NestedDict`.\n\n    Note that `default` has higher priority than `default_factory`.\n\n    Args:\n        name:\n        default:\n\n    Returns:\n        value:\n            If `NestedDict` does not contain `name`, return `default`.\n            If `default` is not specified, return `default_factory()`.\n\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\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    \"\"\"\n\n    delimiter = self.getattr(\"delimiter\", \".\")\n    if fallback is None:\n        fallback = self.getattr(\"fallback\", False)\n    fallback_name = name.split(delimiter)[-1] if isinstance(name, str) else name\n    fallback_value = Null\n    try:\n        while isinstance(name, str) and delimiter in name:\n            if fallback and fallback_name in self:\n                fallback_value = self.get(fallback_name)\n            name, rest = name.split(delimiter, 1)\n            self, name = self[name], rest  # pylint: disable=W0642\n    except (KeyError, AttributeError, TypeError):\n        if fallback and fallback_value is not Null:\n            return fallback_value\n        if default is not Null:\n            return default\n        raise KeyError(name) from None\n    if (fallback and fallback_value is not Null) and (not isinstance(self, Iterable) or name not in self):\n        return fallback_value\n    # if value is a python dict\n    if not isinstance(self, NestedDict):\n        if name not in self and default is not Null:\n            return default\n        return self[name]\n    return 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(self, other: Mapping | Iterable | PathStr, recursive: bool = True) -> Self:  # pylint: disable=W0221\n    r\"\"\"\n    Intersection of `NestedDict` and `other`.\n\n    Args:\n        other (Mapping | Iterable | PathStr):\n        recursive (bool):\n\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    \"\"\"\n\n    if isinstance(other, (PathLike, str, bytes)):\n        other = self.load(other)\n    if isinstance(other, (Mapping,)):\n        other = self.empty(other).items()\n    if not isinstance(other, Iterable):\n        raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n    return self.empty(self._intersect(self, other, recursive))\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:\n    r\"\"\"\n    Pop value from `NestedDict`.\n\n    Args:\n        name:\n        default:\n\n    Returns:\n        value: If `NestedDict` does not contain `name`, return `default`.\n\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    \"\"\"\n\n    delimiter = self.getattr(\"delimiter\", \".\")\n    try:\n        while isinstance(name, str) and delimiter in name:\n            name, rest = name.split(delimiter, 1)\n            self, name = self[name], rest  # pylint: disable=W0642\n    except (AttributeError, TypeError):\n        raise KeyError(name) from None\n    if not isinstance(self, dict) or name not in self:\n        if default is not Null:\n            return default\n        raise KeyError(name)\n    return 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\n    self,\n    name: Any,\n    value: Any,\n    convert_mapping: bool | None = None,\n) -> None:\n    r\"\"\"\n    Set value of `NestedDict`.\n\n    Args:\n        name:\n        value:\n        convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n            Defaults to self.convert_mapping.\n\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\n\n    full_name = name\n    delimiter = self.getattr(\"delimiter\", \".\")\n    if convert_mapping is None:\n        convert_mapping = self.getattr(\"convert_mapping\", False)\n    default_factory = self.getattr(\"default_factory\", self.empty)\n    try:\n        while isinstance(name, str) and delimiter in name:\n            name, rest = name.split(delimiter, 1)\n            if name in dir(self) and isinstance(getattr(self.__class__, name), (property, cached_property)):\n                self, name = getattr(self, name), rest\n            elif name not in self and isinstance(self, Mapping):\n                default = (\n                    self.__missing__(name, default_factory()) if hasattr(self, \"__missing__\") else default_factory()\n                )\n                self, name = default, rest\n            else:\n                self, name = self[name], rest\n            if isinstance(self, NestedDict):\n                default_factory = self.getattr(\"default_factory\", self.empty)\n    except (AttributeError, TypeError):\n        raise KeyError(name) from None\n\n    if (\n        convert_mapping\n        and not isinstance(value, default_factory if isinstance(default_factory, type) else type(self))\n        and not isinstance(value, Variable)\n    ):\n        if isinstance(value, Mapping):\n            try:\n                value = default_factory(**value)\n            except TypeError:\n                value = default_factory(value)\n        if isinstance(value, list):\n            value = [default_factory(v) if isinstance(v, Mapping) else v for v in value]\n        if isinstance(value, tuple):\n            value = tuple(default_factory(v) if isinstance(v, Mapping) else v for v in value)\n        if isinstance(value, set):\n            value = {default_factory(v) if isinstance(v, Mapping) else v for v in list(value)}\n    if isinstance(self, NestedDict):\n        super().set(name, value)\n    elif isinstance(self, Mapping):\n        dict.__setitem__(self, name, value)\n    else:\n        raise ValueError(\n            f\"Cannot set `{full_name}` to `{value}`, as `{delimiter.join(full_name.split(delimiter)[:-1])}={self}`.\"\n        )\n
"},{"location":"nested_dict/#chanfig.NestedDict.setdefault","title":"setdefault(name, value, convert_mapping=None)","text":"

Set default value for 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

Returns:

Name Type Description value Any

If NestedDict does not contain name, return value.

Examples:

Python Console Session
>>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\", \"n.a.b.c\": 1})\n>>> d.setdefault(\"d.i\", 1031)\n1031\n>>> d.setdefault(\"i.d\", \"chang\")\n1013\n>>> d.setdefault(\"f.n\", 1013)\n'chang'\n>>> d.setdefault(\"n.a.b.d\", 2)\n2\n
Source code in chanfig/nested_dict.py Python
def setdefault(  # type: ignore[override]  # pylint: disable=R0912,W0221\n    self,\n    name: Any,\n    value: Any,\n    convert_mapping: bool | None = None,\n) -> Any:\n    r\"\"\"\n    Set default value for `NestedDict`.\n\n    Args:\n        name:\n        value:\n        convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n            Defaults to self.convert_mapping.\n\n    Returns:\n        value: If `NestedDict` does not contain `name`, return `value`.\n\n    Examples:\n        >>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\", \"n.a.b.c\": 1})\n        >>> d.setdefault(\"d.i\", 1031)\n        1031\n        >>> d.setdefault(\"i.d\", \"chang\")\n        1013\n        >>> d.setdefault(\"f.n\", 1013)\n        'chang'\n        >>> d.setdefault(\"n.a.b.d\", 2)\n        2\n    \"\"\"\n    # pylint: disable=W0642\n\n    full_name = name\n    delimiter = self.getattr(\"delimiter\", \".\")\n    if convert_mapping is None:\n        convert_mapping = self.getattr(\"convert_mapping\", False)\n    default_factory = self.getattr(\"default_factory\", self.empty)\n    try:\n        while isinstance(name, str) and delimiter in name:\n            name, rest = name.split(delimiter, 1)\n            if name in dir(self) and isinstance(getattr(self.__class__, name), (property, cached_property)):\n                self, name = getattr(self, name), rest\n            elif name not in self and isinstance(self, Mapping):\n                default = (\n                    self.__missing__(name, default_factory()) if hasattr(self, \"__missing__\") else default_factory()\n                )\n                self, name = default, rest\n            else:\n                self, name = self[name], rest\n            if isinstance(self, NestedDict):\n                default_factory = self.getattr(\"default_factory\", self.empty)\n    except (AttributeError, TypeError):\n        raise KeyError(name) from None\n\n    if isinstance(self, NestedDict) and name in self:\n        return super().get(name)\n    elif isinstance(self, Mapping) and name in self:\n        dict.__getitem__(self, name)\n\n    if (\n        convert_mapping\n        and isinstance(value, Mapping)\n        and not isinstance(value, default_factory if isinstance(default_factory, type) else type(self))\n        and not isinstance(value, Variable)\n    ):\n        try:\n            value = default_factory(**value)\n        except TypeError:\n            value = default_factory(value)\n    if isinstance(self, NestedDict):\n        super().set(name, value)\n    elif isinstance(self, Mapping):\n        dict.__setitem__(self, name, value)\n    else:\n        raise ValueError(\n            f\"Cannot set `{full_name}` to `{value}`, as `{delimiter.join(full_name.split(delimiter)[:-1])}={self}`.\"\n        )\n    return value\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]\n
Source code in chanfig/nested_dict.py Python
def sort(self, key: Callable | None = None, reverse: bool = False, recursive: bool = True) -> Self:\n    r\"\"\"\n    Sort `NestedDict`.\n\n    Args:\n        recursive (bool): Whether to apply `sort` recursively.\n\n    Returns:\n        (NestedDict):\n\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]\n    \"\"\"\n\n    if recursive:\n        for value in self.values():\n            if isinstance(value, FlatDict):\n                value.sort(key=key, reverse=reverse)\n    return super().sort(key=key, reverse=reverse)\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:\n    r\"\"\"\n    Validate `NestedDict`.\n\n    Raises:\n        TypeError: If `Variable` has invalid type.\n        ValueError: If `Variable` has invalid value.\n\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    \"\"\"\n\n    self.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 new parse_config and parse method to parse command-line arguments to CHANfiG.Config object.

parse_config will read the configuration and determine possible arguments and their types. This makes it more favourable than parse as it has strict name checking.

parse will try to parse any command-line arguments, even if they are not pre-defined by 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.

ConfigParser override parse_args method to ensure the output is a NestedDict.

Source code in chanfig/parser.py Python
class ConfigParser(ArgumentParser):  # pylint: disable=C0115\n    r\"\"\"\n    Parser to parse command-line arguments for CHANfiG.\n\n    `ConfigParser` is a subclass of `argparse.ArgumentParser`.\n    It provides new `parse_config` and `parse` method to parse command-line arguments to `CHANfiG.Config` object.\n\n    `parse_config` will read the configuration and determine possible arguments and their types.\n    This makes it more favourable than `parse` as it has strict name checking.\n\n    `parse` will try to parse any command-line arguments, even if they are not pre-defined by `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\n    `ConfigParser` override `parse_args` method to ensure the output is a `NestedDict`.\n    \"\"\"\n\n    def __init__(self, *args: Any, **kwargs: Any):\n        super().__init__(*args, **kwargs)\n        self._registries[\"action\"][None] = StoreAction\n        self._registries[\"action\"][\"store\"] = StoreAction\n\n    def parse_config(  # pylint: disable=R0912\n        self,\n        args: Sequence[str] | None = None,\n        config: Config | None = None,\n        default_config: str | None = None,\n        no_default_config_action: str = \"raise\",\n    ) -> Config:\n        r\"\"\"\n        Parse the arguments for `Config`.\n\n        You may optionally specify a name for `default_config`,\n        and CHANfiG will read the file under this name.\n\n        There are three levels of config:\n\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\n        Higher levels override lower levels (i.e. 3 > 2 > 1).\n\n        Args:\n            args (Sequence[str] | None, optional): Command-line arguments. Defaults to `None`.\n            config (NestedDict | None, optional): existing configuration.\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\n        Returns:\n            config: The parsed `Config`.\n\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\n        See Also:\n            [`parse`][chanfig.ConfigParser.parse]: Parse all command-line arguments.\n\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\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        \"\"\"\n\n        if args is None:\n            args = sys.argv[1:]\n\n        if config is None:\n            raise ValueError(\"config must be specified\")\n        self.add_config_arguments(config)\n\n        if no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\n            raise ValueError(\n                f\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n            )\n\n        # parse the command-line arguments\n        parsed = self.parse_args(args)\n\n        # parse the default config file\n        if default_config is not None:\n            parsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n\n        if config.getattr(\"parser\", None) is not self:\n            config.setattr(\"parser\", self)\n        return config.merge(parsed)\n\n    def parse(  # pylint: disable=R0912\n        self,\n        args: Sequence[str] | None = None,\n        config: Config | None = None,\n        default_config: str | None = None,\n        no_default_config_action: str = \"raise\",\n    ) -> Config:\n        r\"\"\"\n        Parse the arguments for `Config`.\n\n        You may optionally specify a name for `default_config`,\n        and CHANfiG will read the file under this name.\n\n        There are three levels of config:\n\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\n        Higher levels override lower levels (i.e. 3 > 2 > 1).\n\n        Args:\n            args (Sequence[str] | None, optional): Command-line arguments. Defaults to `None`.\n            config (NestedDict | None, optional): existing configuration.\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\n        Returns:\n            config: The parsed `Config`.\n\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\n        See Also:\n            [`parse_config`][chanfig.ConfigParser.parse_config]: Only parse valid config arguments.\n\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\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\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\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\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\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        \"\"\"\n\n        if args is None:\n            args = sys.argv[1:]\n\n        if config is None:\n            from .config import Config  # pylint: disable=C0415\n\n            config = Config()\n        else:\n            self.add_config_arguments(config)\n\n        if no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\n            raise ValueError(\n                f\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n            )\n\n        # add the command-line arguments\n        key_value_args = []\n        for arg in args:\n            if args == \"--\":\n                break\n            if arg.startswith(\"-\"):\n                key_value_args.append(arg.split(\"=\", maxsplit=1))\n            else:\n                if not key_value_args:\n                    continue\n                key_value_args[-1].append(arg)\n        for key_value in key_value_args:\n            if key_value[0] not in self:\n                if len(key_value) > 2:\n                    self.add_argument(key_value[0], nargs=\"+\")\n                else:\n                    self.add_argument(key_value[0])\n\n        # parse the command-line arguments\n        parsed = self.parse_args(args)\n\n        # parse the default config file\n        if default_config is not None:\n            parsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n\n        if config.getattr(\"parser\", None) is not self:\n            config.setattr(\"parser\", self)\n        return config.merge(parsed)\n\n    def parse_args(  # type: ignore[override]\n        self, args: Sequence[str] | None = None, namespace: NestedDict | None = None, eval_str: bool = True\n    ) -> NestedDict:\n        r\"\"\"\n        Parse command line arguments and convert types.\n\n        This function first calls `ArgumentParser.parse_args` to parse command line arguments.\n        It ensures the returned parsed values is stored in a NestedDict instance.\n        If `eval_str` is specified, it also performs `literal_eval` on all `str` values.\n\n        Args:\n            args (Sequence[str] | None, optional): Command-line arguments. Defaults to `None`.\n            namespace (NestedDict | None, optional): existing configuration.\n            eval_str (bool, optional): Whether to evaluate string values.\n        \"\"\"\n        parsed: dict | Namespace = super().parse_args(args, namespace)\n        if isinstance(parsed, Namespace):\n            parsed = vars(parsed)\n        if not isinstance(parsed, NestedDict):\n            parsed = NestedDict({key: value for key, value in parsed.items() if value is not Null})\n        if eval_str:\n            for key, value in parsed.all_items():\n                if isinstance(value, str):\n                    with suppress(TypeError, ValueError, SyntaxError):\n                        value = literal_eval(value)\n                    parsed[key] = value\n        return parsed\n\n    def add_config_arguments(self, config: Config):\n        for key, dtype in get_annotations(config).items():\n            self.add_config_argument(key, dtype=dtype)\n        for key, value in config.all_items():\n            self.add_config_argument(key, value)\n\n    def add_config_argument(self, key, value: Any | None = None, dtype: type | None = None):\n        if dtype is None:\n            if isinstance(value, Variable):\n                dtype = value._type or value.dtype  # pylint: disable=W0212\n            elif isinstance(value, Field):\n                dtype = value.type\n            elif value is not None:\n                dtype = type(value)\n        if _should_collect_from_parameters(dtype):\n            args = get_args(dtype)\n            if len(args) == 2 and NoneType in args:\n                dtype = args[0] if args[0] is not NoneType else args[1]\n        name = \"--\" + key\n        if name not in self:\n            help = None  # pylint: disable=W0622\n            if isinstance(value, Variable):\n                help = value._help  # pylint: disable=W0212\n            elif isinstance(value, Field):\n                help = value.metadata.get(\"help\")\n            if dtype is None or not isclass(dtype):\n                return self.add_argument(name, help=help, dest=key)\n            if issubclass(dtype, (list, tuple, dict, set)):\n                return self.add_argument(name, type=dtype, nargs=\"+\", help=help, dest=key)\n            if issubclass(dtype, bool):\n                return self.add_argument(name, type=parse_bool, help=help, dest=key)\n            return self.add_argument(name, type=dtype, help=help, dest=key)\n\n    def merge_default_config(self, parsed, default_config: str, no_default_config_action: str = \"raise\") -> NestedDict:\n        message = f\"default_config is set to {default_config}, but not found in args.\"\n        if default_config in parsed:\n            path = parsed[default_config]\n            warn(f\"Config has 'default_config={path}' specified, its values will override values in Config\")\n            return NestedDict.load(path).merge(parsed)\n        if no_default_config_action == \"ignore\":\n            pass\n        elif no_default_config_action == \"warn\":\n            warn(message, category=RuntimeWarning, stacklevel=2)\n        else:\n            raise RuntimeError(message)\n        return parsed\n\n    @staticmethod\n    def identity(string):\n        r\"\"\"\n        https://stackoverflow.com/questions/69896931/cant-pickle-local-object-argumentparser-init-locals-identity\n        \"\"\"\n\n        return string\n\n    def __contains__(self, name: str):\n        if name in self._option_string_actions:\n            return True\n        return False\n
"},{"location":"parser/#chanfig.ConfigParser.identity","title":"identity(string) staticmethod","text":"Source code in chanfig/parser.py Python
@staticmethod\ndef identity(string):\n    r\"\"\"\n    https://stackoverflow.com/questions/69896931/cant-pickle-local-object-argumentparser-init-locals-identity\n    \"\"\"\n\n    return 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 Sequence[str] | None

Command-line arguments. Defaults to None.

None config NestedDict | None

existing configuration.

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\n    self,\n    args: Sequence[str] | None = None,\n    config: Config | None = None,\n    default_config: str | None = None,\n    no_default_config_action: str = \"raise\",\n) -> Config:\n    r\"\"\"\n    Parse the arguments for `Config`.\n\n    You may optionally specify a name for `default_config`,\n    and CHANfiG will read the file under this name.\n\n    There are three levels of config:\n\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\n    Higher levels override lower levels (i.e. 3 > 2 > 1).\n\n    Args:\n        args (Sequence[str] | None, optional): Command-line arguments. Defaults to `None`.\n        config (NestedDict | None, optional): existing configuration.\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\n    Returns:\n        config: The parsed `Config`.\n\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\n    See Also:\n        [`parse_config`][chanfig.ConfigParser.parse_config]: Only parse valid config arguments.\n\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\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\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\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\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\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    \"\"\"\n\n    if args is None:\n        args = sys.argv[1:]\n\n    if config is None:\n        from .config import Config  # pylint: disable=C0415\n\n        config = Config()\n    else:\n        self.add_config_arguments(config)\n\n    if no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\n        raise ValueError(\n            f\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n        )\n\n    # add the command-line arguments\n    key_value_args = []\n    for arg in args:\n        if args == \"--\":\n            break\n        if arg.startswith(\"-\"):\n            key_value_args.append(arg.split(\"=\", maxsplit=1))\n        else:\n            if not key_value_args:\n                continue\n            key_value_args[-1].append(arg)\n    for key_value in key_value_args:\n        if key_value[0] not in self:\n            if len(key_value) > 2:\n                self.add_argument(key_value[0], nargs=\"+\")\n            else:\n                self.add_argument(key_value[0])\n\n    # parse the command-line arguments\n    parsed = self.parse_args(args)\n\n    # parse the default config file\n    if default_config is not None:\n        parsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n\n    if config.getattr(\"parser\", None) is not self:\n        config.setattr(\"parser\", self)\n    return config.merge(parsed)\n
"},{"location":"parser/#chanfig.ConfigParser.parse_args","title":"parse_args(args=None, namespace=None, eval_str=True)","text":"

Parse command line arguments and convert types.

This function first calls ArgumentParser.parse_args to parse command line arguments. It ensures the returned parsed values is stored in a NestedDict instance. If eval_str is specified, it also performs literal_eval on all str values.

Parameters:

Name Type Description Default args Sequence[str] | None

Command-line arguments. Defaults to None.

None namespace NestedDict | None

existing configuration.

None eval_str bool

Whether to evaluate string values.

True Source code in chanfig/parser.py Python
def parse_args(  # type: ignore[override]\n    self, args: Sequence[str] | None = None, namespace: NestedDict | None = None, eval_str: bool = True\n) -> NestedDict:\n    r\"\"\"\n    Parse command line arguments and convert types.\n\n    This function first calls `ArgumentParser.parse_args` to parse command line arguments.\n    It ensures the returned parsed values is stored in a NestedDict instance.\n    If `eval_str` is specified, it also performs `literal_eval` on all `str` values.\n\n    Args:\n        args (Sequence[str] | None, optional): Command-line arguments. Defaults to `None`.\n        namespace (NestedDict | None, optional): existing configuration.\n        eval_str (bool, optional): Whether to evaluate string values.\n    \"\"\"\n    parsed: dict | Namespace = super().parse_args(args, namespace)\n    if isinstance(parsed, Namespace):\n        parsed = vars(parsed)\n    if not isinstance(parsed, NestedDict):\n        parsed = NestedDict({key: value for key, value in parsed.items() if value is not Null})\n    if eval_str:\n        for key, value in parsed.all_items():\n            if isinstance(value, str):\n                with suppress(TypeError, ValueError, SyntaxError):\n                    value = literal_eval(value)\n                parsed[key] = value\n    return parsed\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 Sequence[str] | None

Command-line arguments. Defaults to None.

None config NestedDict | None

existing configuration.

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\n    self,\n    args: Sequence[str] | None = None,\n    config: Config | None = None,\n    default_config: str | None = None,\n    no_default_config_action: str = \"raise\",\n) -> Config:\n    r\"\"\"\n    Parse the arguments for `Config`.\n\n    You may optionally specify a name for `default_config`,\n    and CHANfiG will read the file under this name.\n\n    There are three levels of config:\n\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\n    Higher levels override lower levels (i.e. 3 > 2 > 1).\n\n    Args:\n        args (Sequence[str] | None, optional): Command-line arguments. Defaults to `None`.\n        config (NestedDict | None, optional): existing configuration.\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\n    Returns:\n        config: The parsed `Config`.\n\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\n    See Also:\n        [`parse`][chanfig.ConfigParser.parse]: Parse all command-line arguments.\n\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\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    \"\"\"\n\n    if args is None:\n        args = sys.argv[1:]\n\n    if config is None:\n        raise ValueError(\"config must be specified\")\n    self.add_config_arguments(config)\n\n    if no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\n        raise ValueError(\n            f\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n        )\n\n    # parse the command-line arguments\n    parsed = self.parse_args(args)\n\n    # parse the default config file\n    if default_config is not None:\n        parsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n\n    if config.getattr(\"parser\", None) is not self:\n        config.setattr(\"parser\", self)\n    return config.merge(parsed)\n
"},{"location":"registry/","title":"Registry","text":""},{"location":"registry/#chanfig.registry.ConfigRegistry","title":"ConfigRegistry","text":"

Bases: Registry

ConfigRegistry for components that can be initialised with a config.

ConfigRegistry is particularly useful when you want to construct a component from a configuration, such as a Hugginface Transformers model.

See Also

Registry: General purpose Registry.

Examples:

Python Console Session
>>> from dataclasses import dataclass, field\n>>> @dataclass\n... class Config:\n...     a: int\n...     b: int\n...     mode: str = \"proj\"\n>>> registry = ConfigRegistry(key=\"mode\")\n>>> @registry.register(\"proj\")\n... class Proj:\n...     def __init__(self, config):\n...         self.a = config.a\n...         self.b = config.b\n>>> @registry.register(\"inv\")\n... class Inv:\n...     def __init__(self, config):\n...         self.a = config.b\n...         self.b = config.a\n>>> registry\nConfigRegistry(\n  ('proj'): <class 'chanfig.registry.Proj'>\n  ('inv'): <class 'chanfig.registry.Inv'>\n)\n>>> config = Config(a=0, b=1)\n>>> module = registry.build(config)\n>>> module.a, module.b\n(0, 1)\n>>> config = Config(a=0, b=1, mode=\"inv\")\n>>> module = registry.build(config)\n>>> module.a, module.b\n(1, 0)\n>>> @dataclass\n... class ModuleConfig:\n...     a: int = 0\n...     b: int = 1\n...     mode: str = \"proj\"\n>>> @dataclass\n... class NestedConfig:\n...     module: ModuleConfig = field(default_factory=ModuleConfig)\n>>> nested_registry = ConfigRegistry(key=\"module.mode\")\n>>> @nested_registry.register(\"proj\")\n... class Proj:\n...     def __init__(self, config):\n...         self.a = config.module.a\n...         self.b = config.module.b\n>>> @nested_registry.register(\"inv\")\n... class Inv:\n...     def __init__(self, config):\n...         self.a = config.module.b\n...         self.b = config.module.a\n>>> nested_config = NestedConfig()\n>>> module = nested_registry.build(nested_config)\n>>> module.a, module.b\n(0, 1)\n
Source code in chanfig/registry.py Python
class ConfigRegistry(Registry):\n    \"\"\"\n    `ConfigRegistry` for components that can be initialised with a `config`.\n\n    `ConfigRegistry` is particularly useful when you want to construct a component from a configuration, such as a\n    Hugginface Transformers model.\n\n    See Also:\n        [`Registry`][chanfig.Registry]: General purpose Registry.\n\n    Examples:\n        >>> from dataclasses import dataclass, field\n        >>> @dataclass\n        ... class Config:\n        ...     a: int\n        ...     b: int\n        ...     mode: str = \"proj\"\n        >>> registry = ConfigRegistry(key=\"mode\")\n        >>> @registry.register(\"proj\")\n        ... class Proj:\n        ...     def __init__(self, config):\n        ...         self.a = config.a\n        ...         self.b = config.b\n        >>> @registry.register(\"inv\")\n        ... class Inv:\n        ...     def __init__(self, config):\n        ...         self.a = config.b\n        ...         self.b = config.a\n        >>> registry\n        ConfigRegistry(\n          ('proj'): <class 'chanfig.registry.Proj'>\n          ('inv'): <class 'chanfig.registry.Inv'>\n        )\n        >>> config = Config(a=0, b=1)\n        >>> module = registry.build(config)\n        >>> module.a, module.b\n        (0, 1)\n        >>> config = Config(a=0, b=1, mode=\"inv\")\n        >>> module = registry.build(config)\n        >>> module.a, module.b\n        (1, 0)\n        >>> @dataclass\n        ... class ModuleConfig:\n        ...     a: int = 0\n        ...     b: int = 1\n        ...     mode: str = \"proj\"\n        >>> @dataclass\n        ... class NestedConfig:\n        ...     module: ModuleConfig = field(default_factory=ModuleConfig)\n        >>> nested_registry = ConfigRegistry(key=\"module.mode\")\n        >>> @nested_registry.register(\"proj\")\n        ... class Proj:\n        ...     def __init__(self, config):\n        ...         self.a = config.module.a\n        ...         self.b = config.module.b\n        >>> @nested_registry.register(\"inv\")\n        ... class Inv:\n        ...     def __init__(self, config):\n        ...         self.a = config.module.b\n        ...         self.b = config.module.a\n        >>> nested_config = NestedConfig()\n        >>> module = nested_registry.build(nested_config)\n        >>> module.a, module.b\n        (0, 1)\n    \"\"\"\n\n    @staticmethod\n    def init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211\n        r\"\"\"\n        Constructor of component.\n\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\n        Returns:\n            (Any):\n\n        Examples:\n            >>> class Module:\n            ...     def __init__(self, config, a=None, b=None):\n            ...         self.config = config\n            ...         self.a = config.a if a is None else a\n            ...         self.b = config.b if b is None else b\n            >>> config = NestedDict({\"a\": 0, \"b\": 1})\n            >>> module = ConfigRegistry.init(Module, config, b=2)\n            >>> module.a, module.b\n            (0, 2)\n        \"\"\"\n\n        return cls(config, *args, **kwargs)\n\n    def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]\n        r\"\"\"\n        Build a component.\n\n        Args:\n            config\n\n        Returns:\n            (Any):\n\n        Raises:\n            KeyError: If the component is not registered.\n\n        Examples:\n            >>> from dataclasses import dataclass, field\n            >>> registry = ConfigRegistry(key=\"module.mode\")\n            >>> @registry.register(\"proj\")\n            ... class Proj:\n            ...     def __init__(self, config):\n            ...         self.a = config.module.a\n            ...         self.b = config.module.b\n            >>> @registry.register(\"inv\")\n            ... class Inv:\n            ...     def __init__(self, config):\n            ...         self.a = config.module.b\n            ...         self.b = config.module.a\n            >>> @dataclass\n            ... class ModuleConfig:\n            ...     a: int = 0\n            ...     b: int = 1\n            ...     mode: str = \"proj\"\n            >>> @dataclass\n            ... class Config:\n            ...     module: ModuleConfig = field(default_factory=ModuleConfig)\n            >>> config = Config()\n            >>> module = registry.build(config)\n            >>> type(module)\n            <class 'chanfig.registry.Proj'>\n            >>> module.a, module.b\n            (0, 1)\n            >>> type(module)\n            <class 'chanfig.registry.Proj'>\n        \"\"\"\n\n        key = self.getattr(\"key\")\n        config_ = deepcopy(config)\n\n        while \".\" in key:\n            key, rest = key.split(\".\", 1)\n            config_, key = getattr(config_, key), rest\n        name = getattr(config_, key)\n\n        return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]\n
"},{"location":"registry/#chanfig.registry.ConfigRegistry.build","title":"build(config, *args, **kwargs)","text":"

Build a component.

Returns:

Type Description Any

Raises:

Type Description KeyError

If the component is not registered.

Examples:

Python Console Session
>>> from dataclasses import dataclass, field\n>>> registry = ConfigRegistry(key=\"module.mode\")\n>>> @registry.register(\"proj\")\n... class Proj:\n...     def __init__(self, config):\n...         self.a = config.module.a\n...         self.b = config.module.b\n>>> @registry.register(\"inv\")\n... class Inv:\n...     def __init__(self, config):\n...         self.a = config.module.b\n...         self.b = config.module.a\n>>> @dataclass\n... class ModuleConfig:\n...     a: int = 0\n...     b: int = 1\n...     mode: str = \"proj\"\n>>> @dataclass\n... class Config:\n...     module: ModuleConfig = field(default_factory=ModuleConfig)\n>>> config = Config()\n>>> module = registry.build(config)\n>>> type(module)\n<class 'chanfig.registry.Proj'>\n>>> module.a, module.b\n(0, 1)\n>>> type(module)\n<class 'chanfig.registry.Proj'>\n
Source code in chanfig/registry.py Python
def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]\n    r\"\"\"\n    Build a component.\n\n    Args:\n        config\n\n    Returns:\n        (Any):\n\n    Raises:\n        KeyError: If the component is not registered.\n\n    Examples:\n        >>> from dataclasses import dataclass, field\n        >>> registry = ConfigRegistry(key=\"module.mode\")\n        >>> @registry.register(\"proj\")\n        ... class Proj:\n        ...     def __init__(self, config):\n        ...         self.a = config.module.a\n        ...         self.b = config.module.b\n        >>> @registry.register(\"inv\")\n        ... class Inv:\n        ...     def __init__(self, config):\n        ...         self.a = config.module.b\n        ...         self.b = config.module.a\n        >>> @dataclass\n        ... class ModuleConfig:\n        ...     a: int = 0\n        ...     b: int = 1\n        ...     mode: str = \"proj\"\n        >>> @dataclass\n        ... class Config:\n        ...     module: ModuleConfig = field(default_factory=ModuleConfig)\n        >>> config = Config()\n        >>> module = registry.build(config)\n        >>> type(module)\n        <class 'chanfig.registry.Proj'>\n        >>> module.a, module.b\n        (0, 1)\n        >>> type(module)\n        <class 'chanfig.registry.Proj'>\n    \"\"\"\n\n    key = self.getattr(\"key\")\n    config_ = deepcopy(config)\n\n    while \".\" in key:\n        key, rest = key.split(\".\", 1)\n        config_, key = getattr(config_, key), rest\n    name = getattr(config_, key)\n\n    return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]\n
"},{"location":"registry/#chanfig.registry.ConfigRegistry.init","title":"init(config, *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, config, a=None, b=None):\n...         self.config = config\n...         self.a = config.a if a is None else a\n...         self.b = config.b if b is None else b\n>>> config = NestedDict({\"a\": 0, \"b\": 1})\n>>> module = ConfigRegistry.init(Module, config, b=2)\n>>> module.a, module.b\n(0, 2)\n
Source code in chanfig/registry.py Python
@staticmethod\ndef init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211\n    r\"\"\"\n    Constructor of component.\n\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\n    Returns:\n        (Any):\n\n    Examples:\n        >>> class Module:\n        ...     def __init__(self, config, a=None, b=None):\n        ...         self.config = config\n        ...         self.a = config.a if a is None else a\n        ...         self.b = config.b if b is None else b\n        >>> config = NestedDict({\"a\": 0, \"b\": 1})\n        >>> module = ConfigRegistry.init(Module, config, b=2)\n        >>> module.a, module.b\n        (0, 2)\n    \"\"\"\n\n    return cls(config, *args, **kwargs)\n
"},{"location":"registry/#chanfig.registry.Registry","title":"Registry","text":"

Bases: NestedDict

Registry for components.

Registry provides 3 core functionalities:

  • Register a new component.
  • Lookup for a component.
  • Build a component.

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().

See Also

ConfigRegistry: Optimised for components that can be initialised with a config.

Examples:

Python Console Session
>>> registry = Registry()\n>>> @registry.register\n... @registry.register(\"Module1\", default=True)\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\": 0, \"b\": 1}}\n>>> # registry.register(Module)\n>>> module = registry.build(config[\"module\"])\n>>> type(module)\n<class 'chanfig.registry.Module'>\n>>> module.a, module.b\n(0, 1)\n>>> config = {\"module\": {\"name\": \"NE\", \"a\": 1, \"b\": 0}}\n>>> module = registry.build(config[\"module\"])\n>>> module.a, module.b\n(1, 0)\n
Source code in chanfig/registry.py Python
class Registry(NestedDict):\n    \"\"\"\n    `Registry` for components.\n\n    `Registry` provides 3 core functionalities:\n\n    - Register a new component.\n    - Lookup for a component.\n    - Build a component.\n\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\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\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\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\n    Notes:\n        `Registry` inherits from `NestedDict`.\n\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\n    See Also:\n        [`ConfigRegistry`][chanfig.ConfigRegistry]: Optimised for components that can be initialised with a `config`.\n\n    Examples:\n        >>> registry = Registry()\n        >>> @registry.register\n        ... @registry.register(\"Module1\", default=True)\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\": 0, \"b\": 1}}\n        >>> # registry.register(Module)\n        >>> module = registry.build(config[\"module\"])\n        >>> type(module)\n        <class 'chanfig.registry.Module'>\n        >>> module.a, module.b\n        (0, 1)\n        >>> config = {\"module\": {\"name\": \"NE\", \"a\": 1, \"b\": 0}}\n        >>> module = registry.build(config[\"module\"])\n        >>> module.a, module.b\n        (1, 0)\n    \"\"\"\n\n    override: bool = False\n    key: str = \"name\"\n    default: Any = Null\n\n    def __init__(\n        self, override: bool | None = None, key: str | None = None, fallback: bool | None = None, default: Any = None\n    ):\n        super().__init__(fallback=fallback)\n        if override is not None:\n            self.setattr(\"override\", override)\n        else:\n            self.setattr(\"override\", self.override)\n        if key is not None:\n            self.setattr(\"key\", key)\n        else:\n            self.setattr(\"key\", self.key)\n        if default is not None:\n            self.setattr(\"default\", default)\n        else:\n            self.setattr(\"default\", self.default)\n\n    def register(\n        self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False\n    ) -> Callable:\n        r\"\"\"\n        Register a new component.\n\n        Args:\n            component: The component to register.\n            name: The name of the component.\n\n        Returns:\n            component: The registered component.\n                Registered component are expected to be `Callable`.\n\n        Raises:\n            ValueError: If the component with the same name already registered and `Registry.override=False`.\n\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        \"\"\"\n\n        if name in self and not (override or self.override):\n            raise ValueError(f\"Component with name {name} already registered.\")\n\n        # Registry.register()\n        if name is not Null:\n            self.set(name, component)\n            if default:\n                self.setattr(\"default\", component)\n            return component\n        # @Registry.register\n        if component is not Null and callable(component) and name is Null:\n            self.set(component.__name__, component)\n            if default:\n                self.setattr(\"default\", component)\n            return component\n\n        # @Registry.register()\n        def decorator(name: Any = Null):\n            @wraps(self.register)\n            def wrapper(component):\n                if name is Null:\n                    self.set(component.__name__, component)\n                else:\n                    self.set(name, component)\n                if default:\n                    self.setattr(\"default\", component)\n                return component\n\n            return wrapper\n\n        return decorator(component)\n\n    def lookup(self, name: str, default: Any = Null) -> Any:\n        r\"\"\"\n        Lookup for a component.\n\n        Args:\n            name:\n\n        Returns:\n            (Any): The component.\n\n        Raises:\n            KeyError: If the component is not registered.\n\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        \"\"\"\n\n        if default is Null:\n            default = self.getattr(\"default\", Null)\n        return self.get(name, default)\n\n    @staticmethod\n    def init(cls: Callable, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211\n        r\"\"\"\n        Constructor of component.\n\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\n        Returns:\n            (Any):\n\n        Examples:\n            >>> class Module:\n            ...     def __init__(self, a, b):\n            ...         self.a = a\n            ...         self.b = b\n            >>> kwargs = {\"a\": 0, \"b\": 1}\n            >>> module = Registry.init(Module, **kwargs)\n            >>> type(module)\n            <class 'chanfig.registry.Module'>\n            >>> module.a, module.b\n            (0, 1)\n        \"\"\"\n\n        return cls(*args, **kwargs)\n\n    def build(self, name: str | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any:\n        r\"\"\"\n        Build a component.\n\n        Args:\n            name (str | MutableMapping):\n                If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`.\n                Note that values in `kwargs` will override values in `name` if its a `MutableMapping`.\n            *args: The arguments to pass to the component.\n            **kwargs: The keyword arguments to pass to the component.\n\n        Returns:\n            (Any):\n\n        Raises:\n            KeyError: If the component is not registered.\n\n        Examples:\n            >>> registry = Registry(key=\"model\")\n            >>> @registry.register\n            ... class Module:\n            ...     def __init__(self, a, b):\n            ...         self.a = a\n            ...         self.b = b\n            >>> config = {\"module\": {\"model\": \"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        \"\"\"\n\n        if isinstance(name, MutableMapping):\n            name = deepcopy(name)\n            name, kwargs = name.pop(self.getattr(\"key\", \"name\")), dict(name, **kwargs)  # type: ignore[arg-type]\n        if name is Null:\n            name, kwargs = kwargs.pop(self.getattr(\"key\"), None), dict(**kwargs)\n        return self.init(self.lookup(name), *args, **kwargs)  # type: ignore[arg-type]\n
"},{"location":"registry/#chanfig.registry.Registry.build","title":"build(name=Null, *args, **kwargs)","text":"

Build a component.

Parameters:

Name Type Description Default name str | MutableMapping

If its a MutableMapping, it must contain key as a member, the rest will be treated as **kwargs. Note that values in kwargs will override values in name if its a MutableMapping.

Null *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(key=\"model\")\n>>> @registry.register\n... class Module:\n...     def __init__(self, a, b):\n...         self.a = a\n...         self.b = b\n>>> config = {\"module\": {\"model\": \"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 | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any:\n    r\"\"\"\n    Build a component.\n\n    Args:\n        name (str | MutableMapping):\n            If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`.\n            Note that values in `kwargs` will override values in `name` if its a `MutableMapping`.\n        *args: The arguments to pass to the component.\n        **kwargs: The keyword arguments to pass to the component.\n\n    Returns:\n        (Any):\n\n    Raises:\n        KeyError: If the component is not registered.\n\n    Examples:\n        >>> registry = Registry(key=\"model\")\n        >>> @registry.register\n        ... class Module:\n        ...     def __init__(self, a, b):\n        ...         self.a = a\n        ...         self.b = b\n        >>> config = {\"module\": {\"model\": \"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    \"\"\"\n\n    if isinstance(name, MutableMapping):\n        name = deepcopy(name)\n        name, kwargs = name.pop(self.getattr(\"key\", \"name\")), dict(name, **kwargs)  # type: ignore[arg-type]\n    if name is Null:\n        name, kwargs = kwargs.pop(self.getattr(\"key\"), None), dict(**kwargs)\n    return self.init(self.lookup(name), *args, **kwargs)  # type: ignore[arg-type]\n
"},{"location":"registry/#chanfig.registry.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\": 0, \"b\": 1}\n>>> module = Registry.init(Module, **kwargs)\n>>> type(module)\n<class 'chanfig.registry.Module'>\n>>> module.a, module.b\n(0, 1)\n
Source code in chanfig/registry.py Python
@staticmethod\ndef init(cls: Callable, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211\n    r\"\"\"\n    Constructor of component.\n\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\n    Returns:\n        (Any):\n\n    Examples:\n        >>> class Module:\n        ...     def __init__(self, a, b):\n        ...         self.a = a\n        ...         self.b = b\n        >>> kwargs = {\"a\": 0, \"b\": 1}\n        >>> module = Registry.init(Module, **kwargs)\n        >>> type(module)\n        <class 'chanfig.registry.Module'>\n        >>> module.a, module.b\n        (0, 1)\n    \"\"\"\n\n    return cls(*args, **kwargs)\n
"},{"location":"registry/#chanfig.registry.Registry.lookup","title":"lookup(name, default=Null)","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, default: Any = Null) -> Any:\n    r\"\"\"\n    Lookup for a component.\n\n    Args:\n        name:\n\n    Returns:\n        (Any): The component.\n\n    Raises:\n        KeyError: If the component is not registered.\n\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    \"\"\"\n\n    if default is Null:\n        default = self.getattr(\"default\", Null)\n    return self.get(name, default)\n
"},{"location":"registry/#chanfig.registry.Registry.register","title":"register(component=Null, name=Null, override=False, default=False)","text":"

Register a new component.

Parameters:

Name Type Description Default component Any

The component to register.

Null name Any

The name of the component.

Null

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(\n    self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False\n) -> Callable:\n    r\"\"\"\n    Register a new component.\n\n    Args:\n        component: The component to register.\n        name: The name of the component.\n\n    Returns:\n        component: The registered component.\n            Registered component are expected to be `Callable`.\n\n    Raises:\n        ValueError: If the component with the same name already registered and `Registry.override=False`.\n\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    \"\"\"\n\n    if name in self and not (override or self.override):\n        raise ValueError(f\"Component with name {name} already registered.\")\n\n    # Registry.register()\n    if name is not Null:\n        self.set(name, component)\n        if default:\n            self.setattr(\"default\", component)\n        return component\n    # @Registry.register\n    if component is not Null and callable(component) and name is Null:\n        self.set(component.__name__, component)\n        if default:\n            self.setattr(\"default\", component)\n        return component\n\n    # @Registry.register()\n    def decorator(name: Any = Null):\n        @wraps(self.register)\n        def wrapper(component):\n            if name is Null:\n                self.set(component.__name__, component)\n            else:\n                self.set(name, component)\n            if default:\n                self.setattr(\"default\", component)\n            return component\n\n        return wrapper\n\n    return decorator(component)\n
"},{"location":"utils/","title":"Utilities","text":"

Bases: type

Metaclass for Singleton Classes.

Source code in chanfig/utils.py Python
class Singleton(type):\n    r\"\"\"\n    Metaclass for Singleton Classes.\n    \"\"\"\n\n    __instances__: Mapping[type, object] = {}\n\n    def __call__(cls, *args: Any, **kwargs: Any):\n        if cls not in cls.__instances__:\n            cls.__instances__[cls] = super().__call__(*args, **kwargs)  # type: ignore[index]\n        return 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):\n    r\"\"\"\n    NULL class.\n\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\n    `Null` is an instance of `NULL` and is recommended to be used as `obj is Null`.\n    \"\"\"\n\n    def __repr__(self):\n        return \"Null\"\n\n    def __nonzero__(self):\n        return False\n\n    def __len__(self):\n        return 0\n\n    def __call__(self, *args: Any, **kwargs: Any):\n        return self\n\n    def __contains__(self, name):\n        return False\n\n    def __iter__(self):\n        return self\n\n    def __next__(self):\n        raise StopIteration\n\n    def __getattr__(self, name):\n        return self\n\n    def __getitem__(self, index):\n        return self\n

options: heading_level: 0

Bases: JSONEncoder

JSON encoder for Config.

Source code in chanfig/utils.py Python
class JsonEncoder(JSONEncoder):\n    r\"\"\"\n    JSON encoder for Config.\n    \"\"\"\n\n    def default(self, o: Any) -> Any:\n        if hasattr(o, \"__json__\"):\n            return o.__json__()\n        return 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\n    r\"\"\"\n    YAML Dumper for Config.\n    \"\"\"\n\n    def increase_indent(self, flow: bool = False, indentless: bool = False):  # pylint: disable=W0235\n        return 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\n    r\"\"\"\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\n    r\"\"\"\n    Mutable wrapper for immutable objects.\n\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\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\n    Attributes:\n        value: The wrapped value.\n        dtype: The type of the wrapped value.\n\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\n        To temporarily disable this behaviour, you can call context manager `with Variable.unwrapped()`.\n\n        To permanently disable this behaviour, you can call `Variable.unwrap()`.\n\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    \"\"\"\n\n    wrap_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\n\n    def __init__(  # pylint: disable=R0913\n        self,\n        value: Any = Null,\n        type: type | None = None,  # pylint: disable=W0622\n        choices: list | None = None,\n        validator: Callable | None = None,\n        required: bool = False,\n        help: str | None = None,  # pylint: disable=W0622\n    ) -> None:\n        self._storage = [value]\n        self._type = type\n        self._choices = choices\n        self._validator = validator\n        self._required = required\n        self._help = help\n\n    @property  # type: ignore[misc]\n    def __class__(self) -> type:\n        return self.value.__class__ if self.wrap_type else type(self)\n\n    @property\n    def value(self) -> Any:\n        r\"\"\"\n        Fetch the object wrapped in `Variable`.\n        \"\"\"\n\n        return self._storage[0]\n\n    @value.setter\n    def value(self, value) -> None:\n        r\"\"\"\n        Assign value to the object wrapped in `Variable`.\n        \"\"\"\n\n        self.validate(value)\n        self._storage[0] = self._get_value(value)\n\n    @property\n    def dtype(self) -> type:\n        r\"\"\"\n        Data type of the object wrapped in `Variable`.\n\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        \"\"\"\n\n        return self.value.__class__\n\n    @property\n    def storage(self) -> list[Any]:\n        r\"\"\"\n        Storage of `Variable`.\n        \"\"\"\n\n        return self._storage\n\n    @property\n    def type(self) -> type | None:\n        return self._type\n\n    @property\n    def choices(self) -> list | None:\n        return self._choices\n\n    @property\n    def validator(self) -> Callable | None:\n        return self._validator\n\n    @property\n    def required(self) -> bool:\n        return self._required\n\n    @property\n    def help(self) -> str:\n        return self._help or \"\"\n\n    def validate(self, *args) -> None:\n        r\"\"\"\n        Validate if the value is valid.\n        \"\"\"\n\n        if len(args) == 0:\n            value = self.value\n        elif len(args) == 1:\n            value = args[0]\n        else:\n            raise ValueError(\"Too many arguments.\")\n        if self._required and value is Null:\n            raise RuntimeError(\"Value is required.\")\n        if self._type is not None and not isinstance(value, self._type):\n            raise TypeError(f\"Value {value} is not of type {self._type}.\")\n        if self._choices is not None and value not in self._choices:\n            raise ValueError(f\"Value {value} is not in choices {self._choices}.\")\n        if self._validator is not None and not self._validator(value):\n            raise ValueError(f\"Value {value} is not valid.\")\n\n    def get(self) -> Any:\n        r\"\"\"\n        Fetch the object wrapped in `Variable`.\n        \"\"\"\n\n        return self.value\n\n    def set(self, value) -> None:\n        r\"\"\"\n        Assign value to the object wrapped in `Variable`.\n\n        `Variable.set` is extremely useful when you want to change the value without changing the reference.\n\n        In `FlatDict.set`, all assignments of `Variable` calls `Variable.set` Internally.\n        \"\"\"\n\n        self.value = value\n\n    def __get__(self, obj, objtype=None):\n        return self\n\n    def __set__(self, obj, value):\n        self.value = value\n\n    def to(self, cls: Callable) -> Any:  # pylint: disable=C0103\n        r\"\"\"\n        Convert the object wrapped in `Variable` to target `cls`.\n\n        Args:\n            cls: The type to convert to.\n\n        Examples:\n            >>> id = Variable(1013)\n            >>> id.to(float)\n            1013.0\n            >>> id.to(str)\n            '1013.0'\n        \"\"\"\n\n        self.value = cls(self.value)\n        return self\n\n    def int(self) -> int:\n        r\"\"\"\n        Convert the object wrapped in `Variable` to python `int`.\n\n        Examples:\n            >>> id = Variable(1013.0)\n            >>> id.int()\n            1013\n        \"\"\"\n\n        return self.to(int)\n\n    def float(self) -> float:\n        r\"\"\"\n        Convert the object wrapped in `Variable` to python `float`.\n\n        Examples:\n            >>> id = Variable(1013)\n            >>> id.float()\n            1013.0\n        \"\"\"\n\n        return self.to(float)\n\n    def str(self) -> str:\n        r\"\"\"\n        Convert the object wrapped in `Variable` to python `float`.\n\n        Examples:\n            >>> id = Variable(1013)\n            >>> id.str()\n            '1013'\n        \"\"\"\n\n        return self.to(str)\n\n    def wrap(self) -> None:\n        r\"\"\"\n        Wrap the type of `Variable`.\n\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        \"\"\"\n\n        self.wrap_type = True\n\n    def unwrap(self) -> None:\n        r\"\"\"\n        Unwrap the type of `Variable`.\n\n        Examples:\n            >>> id = Variable(1013)\n            >>> id.unwrap()\n            >>> isinstance(id, int)\n            False\n        \"\"\"\n\n        self.wrap_type = False\n\n    @contextmanager\n    def unwrapped(self):\n        r\"\"\"\n        Context manager which temporarily unwrap the `Variable`.\n\n        Examples:\n            >>> id = Variable(1013)\n            >>> isinstance(id, int)\n            True\n            >>> with id.unwrapped():\n            ...    isinstance(id, int)\n            False\n        \"\"\"\n\n        wrap_type = self.wrap_type\n        self.wrap_type = False\n        try:\n            yield self\n        finally:\n            self.wrap_type = wrap_type\n\n    @staticmethod\n    def _get_value(obj) -> Any:\n        if isinstance(obj, Variable):\n            return obj.value\n        return obj\n\n    def __getattr__(self, attr) -> Any:\n        return getattr(self.value, attr)\n\n    def __lt__(self, other) -> bool:\n        return self.value < self._get_value(other)\n\n    def __le__(self, other) -> bool:\n        return self.value <= self._get_value(other)\n\n    def __eq__(self, other) -> bool:\n        return self.value == self._get_value(other)\n\n    def __ne__(self, other) -> bool:\n        return self.value != self._get_value(other)\n\n    def __ge__(self, other) -> bool:\n        return self.value >= self._get_value(other)\n\n    def __gt__(self, other) -> bool:\n        return self.value > self._get_value(other)\n\n    # def __index__(self):\n    #     return self.value.__index__()\n\n    def __invert__(self):\n        return ~self.value\n\n    def __abs__(self):\n        return abs(self.value)\n\n    def __add__(self, other):\n        return Variable(self.value + self._get_value(other))\n\n    def __radd__(self, other):\n        return Variable(self._get_value(other) + self.value)\n\n    def __iadd__(self, other):\n        self.value += self._get_value(other)\n        return self\n\n    def __and__(self, other):\n        return Variable(self.value & self._get_value(other))\n\n    def __rand__(self, other):\n        return Variable(self._get_value(other) & self.value)\n\n    def __iand__(self, other):\n        self.value &= self._get_value(other)\n        return self\n\n    def __floordiv__(self, other):\n        return Variable(self.value // self._get_value(other))\n\n    def __rfloordiv__(self, other):\n        return Variable(self._get_value(other) // self.value)\n\n    def __ifloordiv__(self, other):\n        self.value //= self._get_value(other)\n        return self\n\n    def __mod__(self, other):\n        return Variable(self.value % self._get_value(other))\n\n    def __rmod__(self, other):\n        return Variable(self._get_value(other) % self.value)\n\n    def __imod__(self, other):\n        self.value %= self._get_value(other)\n        return self\n\n    def __mul__(self, other):\n        return Variable(self.value * self._get_value(other))\n\n    def __rmul__(self, other):\n        return Variable(self._get_value(other) * self.value)\n\n    def __imul__(self, other):\n        self.value *= self._get_value(other)\n        return self\n\n    def __matmul__(self, other):\n        return Variable(self.value @ self._get_value(other))\n\n    def __rmatmul__(self, other):\n        return Variable(self._get_value(other) @ self.value)\n\n    def __imatmul__(self, other):\n        self.value @= self._get_value(other)\n        return self\n\n    def __pow__(self, other):\n        return Variable(self.value ** self._get_value(other))\n\n    def __rpow__(self, other):\n        return Variable(self._get_value(other) ** self.value)\n\n    def __ipow__(self, other):\n        self.value **= self._get_value(other)\n        return self\n\n    def __truediv__(self, other):\n        return Variable(self.value / self._get_value(other))\n\n    def __rtruediv__(self, other):\n        return Variable(self._get_value(other) / self.value)\n\n    def __itruediv__(self, other):\n        self.value /= self._get_value(other)\n        return self\n\n    def __sub__(self, other):\n        return Variable(self.value - self._get_value(other))\n\n    def __rsub__(self, other):\n        return Variable(self._get_value(other) - self.value)\n\n    def __isub__(self, other):\n        self.value -= self._get_value(other)\n        return self\n\n    def __copy__(self):\n        return Variable(self.value)\n\n    def __deepcopy__(self, memo: Mapping | None = None):\n        return Variable(copy(self.value))\n\n    def __format__(self, format_spec):\n        return self.value if isinstance(self, str) else format(self.value, format_spec)\n\n    def __iter__(self):\n        return iter(self.value)\n\n    def __next__(self):\n        return next(self.value)\n\n    def __hash__(self):\n        return hash(self.value)\n\n    def __repr__(self):\n        return repr(self.value)\n\n    def __str__(self):\n        return self.value if isinstance(self, str) else str(self.value)\n\n    def __json__(self):\n        return self.value\n\n    def __contains__(self, name):\n        return 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:\n    r\"\"\"\n    Convert the object wrapped in `Variable` to python `float`.\n\n    Examples:\n        >>> id = Variable(1013)\n        >>> id.float()\n        1013.0\n    \"\"\"\n\n    return 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:\n    r\"\"\"\n    Fetch the object wrapped in `Variable`.\n    \"\"\"\n\n    return 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:\n    r\"\"\"\n    Convert the object wrapped in `Variable` to python `int`.\n\n    Examples:\n        >>> id = Variable(1013.0)\n        >>> id.int()\n        1013\n    \"\"\"\n\n    return 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:\n    r\"\"\"\n    Assign value to the object wrapped in `Variable`.\n\n    `Variable.set` is extremely useful when you want to change the value without changing the reference.\n\n    In `FlatDict.set`, all assignments of `Variable` calls `Variable.set` Internally.\n    \"\"\"\n\n    self.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:\n    r\"\"\"\n    Convert the object wrapped in `Variable` to python `float`.\n\n    Examples:\n        >>> id = Variable(1013)\n        >>> id.str()\n        '1013'\n    \"\"\"\n\n    return 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\n    r\"\"\"\n    Convert the object wrapped in `Variable` to target `cls`.\n\n    Args:\n        cls: The type to convert to.\n\n    Examples:\n        >>> id = Variable(1013)\n        >>> id.to(float)\n        1013.0\n        >>> id.to(str)\n        '1013.0'\n    \"\"\"\n\n    self.value = cls(self.value)\n    return 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:\n    r\"\"\"\n    Unwrap the type of `Variable`.\n\n    Examples:\n        >>> id = Variable(1013)\n        >>> id.unwrap()\n        >>> isinstance(id, int)\n        False\n    \"\"\"\n\n    self.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):\n    r\"\"\"\n    Context manager which temporarily unwrap the `Variable`.\n\n    Examples:\n        >>> id = Variable(1013)\n        >>> isinstance(id, int)\n        True\n        >>> with id.unwrapped():\n        ...    isinstance(id, int)\n        False\n    \"\"\"\n\n    wrap_type = self.wrap_type\n    self.wrap_type = False\n    try:\n        yield self\n    finally:\n        self.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:\n    r\"\"\"\n    Validate if the value is valid.\n    \"\"\"\n\n    if len(args) == 0:\n        value = self.value\n    elif len(args) == 1:\n        value = args[0]\n    else:\n        raise ValueError(\"Too many arguments.\")\n    if self._required and value is Null:\n        raise RuntimeError(\"Value is required.\")\n    if self._type is not None and not isinstance(value, self._type):\n        raise TypeError(f\"Value {value} is not of type {self._type}.\")\n    if self._choices is not None and value not in self._choices:\n        raise ValueError(f\"Value {value} is not in choices {self._choices}.\")\n    if self._validator is not None and not self._validator(value):\n        raise 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:\n    r\"\"\"\n    Wrap the type of `Variable`.\n\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    \"\"\"\n\n    self.wrap_type = True\n
"},{"location":"about/","title":"About","text":"

Developed by DanLing on Earth

We are a community of developers, designers, and others from around the world who are working together to make deep learning more accessible.

We are a community of individuals who seek to push the boundaries of what is possible with deep learning.

We are passionate about Deep Learning and the people who use it.

We are DanLing.

"},{"location":"about/license/","title":"GNU AFFERO GENERAL PUBLIC LICENSE","text":"

Version 3, 19 November 2007

Copyright (C) 2007 Free Software Foundation, Inc. https://fsf.org/

Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

"},{"location":"about/license/#preamble","title":"Preamble","text":"

The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software.

The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program\u2013to make sure it remains free software for all its users.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software.

A secondary benefit of defending all users\u2019 freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public.

The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version.

An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license.

The precise terms and conditions for copying, distribution and modification follow.

"},{"location":"about/license/#terms-and-conditions","title":"TERMS AND CONDITIONS","text":""},{"location":"about/license/#0-definitions","title":"0. Definitions.","text":"

\u201cThis License\u201d refers to version 3 of the GNU Affero General Public License.

\u201cCopyright\u201d also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

\u201cThe Program\u201d refers to any copyrightable work licensed under this License. Each licensee is addressed as \u201cyou\u201d. \u201cLicensees\u201d and \u201crecipients\u201d may be individuals or organizations.

To \u201cmodify\u201d a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a \u201cmodified version\u201d of the earlier work or a work \u201cbased on\u201d the earlier work.

A \u201ccovered work\u201d means either the unmodified Program or a work based on the Program.

To \u201cpropagate\u201d a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

To \u201cconvey\u201d a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

An interactive user interface displays \u201cAppropriate Legal Notices\u201d to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

"},{"location":"about/license/#1-source-code","title":"1. Source Code.","text":"

The \u201csource code\u201d for a work means the preferred form of the work for making modifications to it. \u201cObject code\u201d means any non-source form of a work.

A \u201cStandard Interface\u201d means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

The \u201cSystem Libraries\u201d of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A \u201cMajor Component\u201d, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

The \u201cCorresponding Source\u201d for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work\u2019s System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

The Corresponding Source for a work in source code form is that same work.

"},{"location":"about/license/#2-basic-permissions","title":"2. Basic Permissions.","text":"

All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.

"},{"location":"about/license/#3-protecting-users-legal-rights-from-anti-circumvention-law","title":"3. Protecting Users\u2019 Legal Rights From Anti-Circumvention Law.","text":"

No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work\u2019s users, your or third parties\u2019 legal rights to forbid circumvention of technological measures.

"},{"location":"about/license/#4-conveying-verbatim-copies","title":"4. Conveying Verbatim Copies.","text":"

You may convey verbatim copies of the Program\u2019s source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

"},{"location":"about/license/#5-conveying-modified-source-versions","title":"5. Conveying Modified Source Versions.","text":"

You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

  • a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
  • b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to \u201ckeep intact all notices\u201d.
  • c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
  • d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an \u201caggregate\u201d if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation\u2019s users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

"},{"location":"about/license/#6-conveying-non-source-forms","title":"6. Conveying Non-Source Forms.","text":"

You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

  • a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
  • b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
  • c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
  • d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
  • e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

A \u201cUser Product\u201d is either (1) a \u201cconsumer product\u201d, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, \u201cnormally used\u201d refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

\u201cInstallation Information\u201d for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

"},{"location":"about/license/#7-additional-terms","title":"7. Additional Terms.","text":"

\u201cAdditional permissions\u201d are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

  • a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
  • b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
  • c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
  • d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
  • e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
  • f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

All other non-permissive additional terms are considered \u201cfurther restrictions\u201d within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

"},{"location":"about/license/#8-termination","title":"8. Termination.","text":"

You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

"},{"location":"about/license/#9-acceptance-not-required-for-having-copies","title":"9. Acceptance Not Required for Having Copies.","text":"

You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

"},{"location":"about/license/#10-automatic-licensing-of-downstream-recipients","title":"10. Automatic Licensing of Downstream Recipients.","text":"

Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.

An \u201centity transaction\u201d is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party\u2019s predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

"},{"location":"about/license/#11-patents","title":"11. Patents.","text":"

A \u201ccontributor\u201d is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor\u2019s \u201ccontributor version\u201d.

A contributor\u2019s \u201cessential patent claims\u201d are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, \u201ccontrol\u201d includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor\u2019s essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

In the following three paragraphs, a \u201cpatent license\u201d is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To \u201cgrant\u201d such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. \u201cKnowingly relying\u201d means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient\u2019s use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

A patent license is \u201cdiscriminatory\u201d if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

"},{"location":"about/license/#12-no-surrender-of-others-freedom","title":"12. No Surrender of Others\u2019 Freedom.","text":"

If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

"},{"location":"about/license/#13-remote-network-interaction-use-with-the-gnu-general-public-license","title":"13. Remote Network Interaction; Use with the GNU General Public License.","text":"

Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph.

Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License.

"},{"location":"about/license/#14-revised-versions-of-this-license","title":"14. Revised Versions of this License.","text":"

The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License \u201cor any later version\u201d applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation.

If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy\u2019s public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

"},{"location":"about/license/#15-disclaimer-of-warranty","title":"15. Disclaimer of Warranty.","text":"

THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \u201cAS IS\u201d WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

"},{"location":"about/license/#16-limitation-of-liability","title":"16. Limitation of Liability.","text":"

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

"},{"location":"about/license/#17-interpretation-of-sections-15-and-16","title":"17. Interpretation of Sections 15 and 16.","text":"

If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

END OF TERMS AND CONDITIONS

"},{"location":"about/license/#how-to-apply-these-terms-to-your-new-programs","title":"How to Apply These Terms to Your New Programs","text":"

If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the \u201ccopyright\u201d line and a pointer to where the full notice is found.

Text Only
    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as\n    published by the Free Software Foundation, either version 3 of the\n    License, or (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n

Also add information on how to contact you by electronic and paper mail.

If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a \u201cSource\u201d link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements.

You should also get your employer (if you work as a programmer) or school, if any, to sign a \u201ccopyright disclaimer\u201d for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see https://www.gnu.org/licenses/.

"},{"location":"about/privacy/","title":"Privacy Notice","text":"

Last Revised Date

This notice was last updated on May 04, 2024.

"},{"location":"about/privacy/#privacy-notice","title":"Privacy Notice","text":"

This privacy notice for DanLing Team (also known as DanLing) (\u2018we\u2019, \u2018us\u2019, or \u2018our\u2019), describes how and why we might collect, store, use, and/or share (\u2018process\u2019) your information when you use our services (\u2018Services\u2019), such as when you:

  • Visit our website at chanfig.danling.org, or any website of ours that links to this privacy notice

You can change your privacy settings at any time by clicking the button below:

Privacy Control

Questions or concerns? Reading this privacy notice will help you understand your privacy rights and choices. If you do not agree with our policies and practices, please do not use our Services. If you still have any questions or concerns, please contact us at privacy@danling.org.

"},{"location":"about/privacy/#0-summary-of-key-points","title":"0. Summary of Key Points","text":"

This summary provides key points from our privacy notice, but you can find out more details about any of these topics by clicking the link following each key point or by using our table of contents below to find the section you are looking for.

What personal information do we process?

When you visit, use, or navigate our Services, we may process personal information depending on how you interact with us and the Services, the choices you make, and the products and features you use.

What information do we collect?

How do we process your information?

We process your information to provide, improve, and administer our Services, communicate with you, for security and fraud prevention, and to comply with law. We may also process your information for other purposes with your consent. We process your information only when we have a valid legal reason to do so.

How do we process your information?

Do we process any sensitive personal information?

We do not process any sensitive personal information.

Do we collect any information from third parties?

We do not collect any information from third parties.

In what situations and with which parties do we share personal information?

We may share information in specific situations and with specific third parties.

When and with whom we share your personal information?

How do we keep your information safe?

We have organisational and technical processes and procedures in place to protect your personal information.

How do we keep your information safe?

What are your rights?

Depending on where you are located geographically, the applicable privacy law may mean you have certain rights regarding your personal information.

What are your privacy rights?

How do you exercise your rights?

The easiest way to exercise your rights is by contacting the relevant data protection authority in your jurisdiction.

How to exercise your rights

"},{"location":"about/privacy/#1-what-information-do-we-collect","title":"1. What information do we collect?","text":""},{"location":"about/privacy/#personal-information-you-disclose-to-us","title":"Personal information you disclose to us","text":"

In Short

We collect personal information that you provide to us.

We collect personal information that you voluntarily provide to us when you express an interest in obtaining information about us or our products and Services, when you participate in activities on the Services, or otherwise when you contact us.

Sensitive Personal Information

We do not collect any sensitive personal information from you.

"},{"location":"about/privacy/#information-automatically-collected","title":"Information automatically collected","text":"

In Short

Some information \u2014 such as IP address and/or browser and device characteristics \u2014 is collected automatically when you visit our Services.

We automatically collect certain information when you visit, use, or navigate our Services. This information does not reveal your specific identity (like your name or contact information) but may include device and usage information, such as your IP address, browser and device characteristics, operating system, language preferences, referring URLs, device name, country, location, information about how and when you use our Services, and other technical information. This information is primarily needed to maintain the security and operation of our Services, and for our internal analytics and reporting purposes.

Like many businesses, we also collect information through cookies and similar technologies.

The information we collect includes:

  • Identifiers. Identifier is a device and browser-specific unique random string that we generate when you use our Service. This identifier is stored in a cookie on your device, allowing us to identify you across multiple sessions and when you return to our Service. You can delete this cookie at any time by clearing your browser\u2019s cache.
  • Log and Usage Data. Log and usage data is service-related, diagnostic, usage, and performance information our servers automatically collect when you access or use our Services and which we record in log files. Depending on how you interact with us, this log data may include your IP address, device information, browser type, and settings, and information about your activity in the Services (such as the date/time stamps associated with your usage, pages and files viewed, searches and other actions you take such as which features you use), device event information (such as system activity, error reports (sometimes called \u2018crash dumps\u2019) and hardware settings).
  • Device Data. We collect device data such as information about your computer, phone, tablet, or other devices you use to access the Services. Depending on the device used, this device data may include information such as your IP address (or proxy server), device and application identification numbers, location, browser type, hardware model, Internet Service Provider and/or mobile carrier, operating system, and system configuration information.
  • Location Data. We collect location data such as information about your device\u2019s location, which can be either precise or imprecise. How much information we collect depends on the type and settings of the device you use to access the Services. For example, we may use GPS and other technologies to collect geolocation data that tells us your current location (based on your IP address). You can opt out of allowing us to collect this information either by refusing access to the information or by disabling your location settings on your device.
"},{"location":"about/privacy/#categories-of-personal-information-we-collect","title":"Categories of Personal Information We Collect","text":"

We have collected the following categories of personal information in the past twelve (12) months:

Category Examples Collected A. Identifiers Contact details, such as real name, alias, postal address, telephone or mobile contact number, unique personal identifier, online identifier, Internet Protocol address, email address, and account name YES B. Personal information as defined in the California Customer Records statute Name, contact information, education, employment, employment history, and financial information NO C. Protected classification characteristics under state or federal law Gender, age, date of birth, race and ethnicity, national origin, marital status, and other demographic data NO D. Commercial information Transaction information, purchase history, financial details, and payment information NO E. Biometric information Fingerprints and voiceprints NO F. Internet or other similar network activity Browsing history, search history, online behaviour, interest data, and interactions with our and other websites, applications, systems, and advertisements YES G. Geolocation data Device location YES H. Audio, electronic, sensory, or similar information Images and audio, video or call recordings created in connection with our business activities NO I. Professional or employment-related information Business contact details in order to provide you our Services at a business level or job title, work history, and professional qualifications if you apply for a job with us NO J. Education Information Student records and directory information NO K. Inferences drawn from collected personal information Inferences drawn from any of the collected personal information listed above to create a profile or summary about, for example, an individual\u2019s preferences and characteristics YES L. Sensitive personal Information NO

We may also collect other personal information outside of these categories through instances where you interact with us in person, online, or by phone or mail in the context of:

  • Receiving help through our customer support channels;
  • Participation in customer surveys or contests; and
  • Facilitation in the delivery of our Services and to respond to your inquiries.

We will use and retain the collected personal information as needed to provide you with our Services and as necessary to comply with our legal obligations, resolve disputes, and enforce our agreement for the following period:

  • Category A: Indefinitely
  • Category F: Indefinitely
  • Category G: Indefinitely
  • Category K: Indefinitely
"},{"location":"about/privacy/#2-how-do-we-process-your-information","title":"2. How do we process your information?","text":"

In Short

We process your information to provide, improve, and administer our Services, communicate with you, for security and fraud prevention, and to comply with law. We may also process your information for other purposes with your consent.

We process your personal information for a variety of reasons, depending on how you interact with our Services, including:

  • To protect our Services. We may process your information as part of our efforts to keep our Services safe and secure, including fraud monitoring and prevention.
  • To identify user trends. We may process information about how you use our Services to better understand how they are being used so we can improve them.
  • To save or protect an individual\u2019s vital interest. We may process your information when necessary to save or protect an individual\u2019s vital interest, such as to prevent harm.
"},{"location":"about/privacy/#3-what-legal-basis-do-we-have-for-processing-your-information","title":"3. What legal basis do we have for processing your information?","text":"

In Short

We only process your personal information when we believe it is necessary and we have a valid legal reason (i.e. legal basis) to do so under applicable law, like with your consent, to comply with laws, to provide you with services to enter into or fulfil our contractual obligations, to protect your rights, or to fulfil our legitimate business interests.

The General Data Protection Regulation (GDPR) and UK GDPR require us to explain the valid legal bases we rely on in order to process your personal information. As such, we may rely on the following legal bases to process your personal information:

  • Consent. We may process your personal information if you have given us specific consent to use your personal information for a specific purpose. You have the right to withdraw your consent at any time. Learn more about withdrawing your consents.
  • Legitimate Interests. We may process your information when we believe it is reasonably necessary to achieve our legitimate business interests and those interests do not outweigh your interests and fundamental rights and freedoms. For example, we may process your personal information for some of the purposes described in order to:
    • Analyse how our Services are used so we can improve them to engage and retain users
    • Diagnose problems and/or prevent fraudulent activities
  • Legal Obligations. We may process your information where we believe it is necessary for compliance with our legal obligations, such as to cooperate with a law enforcement body or regulatory agency, exercise or defend our legal rights, or disclose your information as evidence in litigation in which we are involved.
  • Vital Interests. We may process your information where we believe it is necessary to protect your vital interests or the vital interests of a third party, such as situations involving potential threats to the safety of any person.

Consent to Processing in Canada

If you are located in Canada, we may be legally permitted under applicable law to process your information without your consent in some exceptional cases, including, for example:

  • If collection is clearly in the interests of an individual and consent cannot be obtained in a timely way
  • For investigations and fraud detection and prevention
  • For business transactions provided certain conditions are met
  • If it is contained in a witness statement and the collection is necessary to assess, process, or settle an insurance claim
  • For identifying injured, ill, or deceased persons and communicating with next of kin
  • If we have reasonable grounds to believe an individual has been, is, or may be victim of financial abuse
  • If it is reasonable to expect collection and use with consent would compromise the availability or the accuracy of the information and the collection is reasonable for purposes related to investigating a breach of an agreement or a contravention of the laws of Canada or a province
  • If disclosure is required to comply with a subpoena, warrant, court order, or rules of the court relating to the production of records
  • If it was produced by an individual in the course of their employment, business, or profession and the collection is consistent with the purposes for which the information was produced
  • If the collection is solely for journalistic, artistic, or literary purposes
  • If the information is publicly available and is specified by the regulations
"},{"location":"about/privacy/#4-when-and-with-whom-do-we-share-your-personal-information","title":"4. When and with whom do we share your personal information?","text":"

In Short

We may share information in specific situations described in this section and/or with the following third parties.

We may use your personal information for our business purposes, such as for undertaking internal research for technological development and demonstration. This is not considered to be \u2018selling\u2019 of your personal information.

Vendors, Consultants, and Other Third-Party Service Providers. We may share your data with third-party vendors, service providers, contractors, or agents (\u2018third parties\u2019) who perform services for us or on our behalf and require access to such information to do that work. We have contracts in place with our third parties, which are designed to help safeguard your personal information. This means that they cannot do anything with your personal information unless we have instructed them to do it. They will also not share your personal information with any organisation apart from us. They also commit to protect the data they hold on our behalf and to retain it for the period we instruct.

The third parties we may share personal information with are as follows:

  • Advertising, Direct Marketing, and Lead Generation
    • Google AdSense
  • Cloud Computing Services
    • Microsoft Azure
    • Amazon Web Services (AWS)
    • Google Cloud Platform (GCP)
  • Communications and Content Delivery Network (CDN) Services
    • Cloudflare
  • Content Optimisation
    • Google Site Search
    • Google Fonts
  • Functionality and Infrastructure Optimisation
    • GitHub Pages
  • User Commenting and Forums
    • Disqus
    • GitHub Issues
    • GitHub Discussions
  • Web and Mobile Analytics
    • Google Analytics

We also may need to share your personal information in the following situations:

  • Business Transfers. We may share or transfer your information in connection with, or during negotiations of, any merger, sale of company assets, financing, or acquisition of all or a portion of our business to another company.

We have disclosed the following categories of personal information for a business purpose in the past twelve (12) months:

Nill

The categories of third parties to whom we sold personal information in the past twelve (12) months:

Nill

The categories of third parties to whom we shared personal information with in the past twelve (12) months:

  • Web and Mobile Analytics
    • Google Analytics
"},{"location":"about/privacy/#5-do-we-use-cookies-and-other-tracking-technologies","title":"5. Do we use cookies and other tracking technologies?","text":"

In Short

We may use cookies and other tracking technologies to collect and store your information.

We also permit third parties and service providers to use online tracking technologies on our Services for analytics and advertising, including to help manage and display advertisements, to tailor advertisements to your interests, or to send abandoned shopping cart reminders (depending on your communication preferences). The third parties and service providers use their technology to provide advertising about products and services tailored to your interests which may appear either on our Services or on other websites.

To the extent these online tracking technologies are deemed to be a \u2018sale\u2019/\u2019sharing\u2019 (which includes targeted advertising, as defined under the applicable laws) under applicable US state laws, you can opt out of these online tracking technologies by clicking the button on the top of this page or the button below:

Privacy Control

"},{"location":"about/privacy/#google-analytics","title":"Google Analytics","text":"

We may share your information with Google Analytics to track and analyse the use of the Services. The Google Analytics Advertising Features that we may use include:

  • Remarketing with Google Analytics
  • Google Display Network Impressions Reporting
  • Google Analytics Demographics and Interests Reporting

To opt out of being tracked by Google Analytics across the Services, visit https://tools.google.com/dlpage/gaoptout. You can opt out of Google Analytics Advertising Features through Ads Settings and Ad Settings for mobile apps.

Other opt out means include http://optout.networkadvertising.org/ and http://www.networkadvertising.org/mobile-choice.

For more information on the privacy practices of Google, please visit the Google Privacy & Terms.

"},{"location":"about/privacy/#6-how-long-do-we-keep-your-information","title":"6. How long do we keep your information?","text":"

In Short

We keep your information for as long as necessary to fulfil the purposes outlined in this privacy notice unless otherwise required by law.

We will only keep your personal information for as long as it is necessary for the purposes set out in this privacy notice, unless a longer retention period is required or permitted by law (such as tax, accounting, or other legal requirements).

When we have no ongoing legitimate business need to process your personal information, we will either delete or anonymise it, or, if this is not possible (for example, because your personal information has been stored in backup archives), then we will securely store your personal information and isolate it from any further processing until deletion is possible.

"},{"location":"about/privacy/#7-how-do-we-keep-your-information-safe","title":"7. How do we keep your information safe?","text":"

In Short

We aim to protect your personal information through a system of organisational and technical security measures.

We have implemented appropriate technical and organisational security measures designed to protect the security of any personal information we process. However, despite our safeguards and efforts to secure your information, no electronic transmission over the internet or information storage technology can be guaranteed to be 100% secure, so we cannot promise or guarantee that hackers, cybercriminals, or other unauthorised third parties will not be able to defeat our security and improperly collect, access, steal, or modify your information. Although we will do our best to protect your personal information, the transmission of personal information to and from our Services is at your own risk. You should only access the Services within a secure environment.

"},{"location":"about/privacy/#8-what-are-your-privacy-rights","title":"8. What are your privacy rights?","text":"

In Short

We strive to protect your privacy rights and choices to the best possible extent under the law.

You have rights under certain data protection laws. However, these rights are not absolute, and in certain cases, we may decline your request as permitted by law. These rights include:

  • Right to know whether or not we are processing your personal data
  • Right to access your personal data
  • Right to correct inaccuracies in your personal data
  • Right to request the deletion of your personal data
  • Right to obtain a copy of the personal data you previously shared with us
  • Right to non-discrimination against you for exercising your rights
  • Right to opt-out
    • of the processing of your personal data if it is used for targeted advertising (or sharing as defined under applicable laws), the sale of personal data, or profiling in furtherance of decisions that produce legal or similarly significant effects (\u2018profiling\u2019) concerning you
    • of the collection of sensitive data and personal data collected through the operation of a voice or facial recognition feature
  • Right to obtain
    • a list of the categories of third parties to which we have disclosed personal data
    • a list of specific third parties to which we have disclosed personal data
  • Right to limit use and disclosure of sensitive personal data
"},{"location":"about/privacy/#how-to-exercise-your-rights","title":"How to exercise your rights","text":"

It is very unlikely that you will be able to exercise the above rights as we do not collect any identifiable personal data from you.

We are unable to reply to and act on data subject access request as we do not save any identifiable information about you, and we will not be able to verify your identity.

If you believe we are unlawfully processing your personal information, you can contact the relevant data protection regulator, state attorney general, or other competent authority in your jurisdiction.

Residency Authority European Economic Area Member State\u2019s data protection supervisory authority United Kingdom Information Commissioner\u2019s Office Australia Office of the Australian Information Commissioner New Zealand Office of New Zealand Privacy Commissioner Canada Office of the Privacy Commissioner of Canada California of the United States California Privacy Protection Agency Switzerland Federal Data Protection and Information Commissioner South Africa Information Regulator"},{"location":"about/privacy/#withdraw-your-consent","title":"Withdraw your consent","text":"

If we are relying on your consent to process your personal information, which may be express and/or implied consent depending on the applicable law, you have the right to withdraw your consent at any time. You can withdraw your consent at any time by clicking the button on the top of this page or the button below:

Privacy Control

However, please note that this will not affect the lawfulness of the processing before its withdrawal nor, when applicable law allows, will it affect the processing of your personal information conducted in reliance on lawful processing grounds other than consent.

"},{"location":"about/privacy/#cookies-and-similar-technologies","title":"Cookies and similar technologies","text":"

Most web browsers are set to accept cookies by default. If you prefer, you can usually choose to set your browser to remove or reject browser cookies. Please note that if you choose to remove or reject cookies, this will NOT affect the availability and functionality of our Services.

"},{"location":"about/privacy/#9-controls-for-do-not-track-features","title":"9. Controls for Do-Not-Track features","text":"

Most web browsers and some mobile operating systems and mobile applications include a Do-Not-Track (\u2018DNT\u2019) feature or setting you can activate to signal your privacy preference not to have data about your online browsing activities monitored and collected. At this stage, no uniform technology standard for recognising and implementing DNT signals has been finalised. Although we cannot promise to honour every DNT signal, we strive to honour all such requests where technically feasible.

California law requires us to let you know how we respond to web browser DNT signals. Because we cannot guarantee to recognise and houour all DNT signals, we do not respond to them at this time.

"},{"location":"about/privacy/#10-do-residents-in-certain-jurisdiction-have-specific-privacy-rights","title":"10. Do residents in certain jurisdiction have specific privacy rights?","text":"

NO.

All men and women are created equal.

We provide the same privacy rights to all individuals, regardless of their location.

Be assured that we will treat you with the same respect and dignity as we would want to be treated.

"},{"location":"about/privacy/#11-how-can-you-review-update-or-delete-the-data-we-collect-from-you","title":"11. How can you review, update, or delete the data we collect from you?","text":"

It is very unlikely that you will be able to review, update, or delete the data we collect from you as we do not collect any identifiable personal data from you, and we will not be able to identify which data belongs to you.

"},{"location":"about/privacy/#12-do-we-make-updates-to-this-notice","title":"12. Do we make updates to this notice?","text":"

In Short

Yes, we will update this notice as necessary to stay compliant with relevant laws.

We may update this privacy notice from time to time. The updated version will be indicated by an updated \u2018Last Revised Date\u2019 at the top of this privacy notice. If we make any material changes, we will notify you by posting the new privacy notice on this page. We are unable to notify you directly as we do not collect any contact information from you. We encourage you to review this privacy notice frequently to stay informed of how we are protecting your information.

"},{"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

ConfigRegistry\u200b\u662f\u200b\u4e00\u4e2a\u200bRegistry\u200b\u7684\u200b\u5b50\u7c7b\u200b\uff0c\u200b\u4ed6\u200b\u4e13\u4e3a\u200b\u4ece\u200b\u4e00\u4e2a\u200bConfig\u200b\u6216\u8005\u200b\u4e00\u4e2a\u200bdataclass\u200b\u6765\u200b\u6784\u5efa\u200b\u4e00\u4e2a\u200b\u5bf9\u8c61\u200b\u800c\u200b\u8bbe\u8ba1\u200b\u3002 \u200b\u53ea\u200b\u9700\u200b\u5728\u200b\u521b\u5efa\u200b\u6ce8\u518c\u8868\u200b\u65f6\u200b\u6307\u5b9a\u200bkey\uff0c\u200b\u7136\u540e\u200b\u5728\u200b\u8c03\u7528\u200bbuild\u200b\u65b9\u6cd5\u200b\u65f6\u200b\u4f20\u5165\u200bconfig\uff0c\u200b\u4f60\u200b\u5c31\u200b\u4f1a\u200b\u5f97\u5230\u200b\u4f60\u200b\u60f3\u8981\u200b\u7684\u200b\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\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

Bash
# CHANfiG, Easier Configuration.\n# Copyright (c) 2022-Present, CHANfiG Contributors\n\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the following licenses:\n# - The Unlicense\n# - GNU Affero General Public License v3.0 or later\n# - GNU General Public License v2.0 or later\n# - BSD 4-Clause \"Original\" or \"Old\" License\n# - MIT License\n# - Apache License 2.0\n\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n# See the LICENSE file for more details.\n\nimport os\n\nfrom chanfig import Config, Variable, configclass\n\n\n@configclass\nclass DataloaderConfig:\n    batch_size: int = 64\n    num_workers: int = 4\n    pin_memory: bool = True\n\n\nclass TestConfig(Config):\n    def __init__(self):\n        super().__init__()\n        dropout = Variable(0.1)\n        self.name = \"CHANfiG\"\n        self.seed = 1013\n        self.activation = \"GELU\"\n        self.optim.lr = 1e-3\n        self.dataloader = DataloaderConfig()\n        self.model.encoder.num_layers = 6\n        self.model.decoder.num_layers = 6\n        self.model.dropout = dropout\n        self.model.encoder.dropout = dropout\n        self.model.decoder.dropout = dropout\n        self.add_argument(\"--batch_size\", dest=\"data.batch_size\")\n        self.add_argument(\"--lr\", dest=\"optim.lr\")\n\n    def post(self):\n        self.id = f\"{self.name}_{self.seed}\"\n\n\nif __name__ == \"__main__\":\n    # config = Config.load('config.yaml')  # read config from a yaml\n    # config = Config.load('config.json')  # read config from a json\n    existing_config = {\"model.encoder.num_layers\": 8}\n    config = TestConfig()\n    config.merge(existing_config)\n    # config.merge('dataset.yaml')  # merge config from a yaml\n    # config.merge('dataset.json', overwrite=False)  # merge config from a json\n    config = config.parse()\n    config.model.decoder.num_layers = 8\n    config.freeze()\n    print(config)\n    # main(config)\n    dir_path = os.path.dirname(os.path.realpath(__file__))\n    print(dir_path)\n    config.save(os.path.join(dir_path, \"config.yaml\"))  # save config to a yaml\n    config.save(os.path.join(dir_path, \"config.json\"))  # save config to a 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

yamljson YAML
activation: GELU\ndataloader:\n  batch_size: 64\n  num_workers: 4\n  pin_memory: true\nid: CHANfiG_1013\nmodel:\n  decoder:\n    dropout: 0.1\n    num_layers: 8\n  dropout: 0.1\n  encoder:\n    dropout: 0.1\n    num_layers: 8\nname: CHANfiG\noptim:\n  lr: 0.001\nseed: 1013\n
JSON
{\n  \"name\": \"CHANfiG\",\n  \"seed\": 1013,\n  \"activation\": \"GELU\",\n  \"optim\": {\n    \"lr\": 0.001\n  },\n  \"dataloader\": {\n    \"batch_size\": 64,\n    \"num_workers\": 4,\n    \"pin_memory\": true\n  },\n  \"model\": {\n    \"encoder\": {\n      \"num_layers\": 8,\n      \"dropout\": 0.1\n    },\n    \"decoder\": {\n      \"num_layers\": 8,\n      \"dropout\": 0.1\n    },\n    \"dropout\": 0.1\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":"\u5b89\u88c5\u200b pypi \u200b\u4e0a\u200b\u6700\u8fd1\u200b\u7684\u200b\u7a33\u5b9a\u200b\u7248\u672c\u200b\u4ece\u200b\u6e90\u7801\u200b\u5b89\u88c5\u200b\u6700\u65b0\u200b\u7684\u200b\u7248\u672c\u200b Bash
pip install chanfig\n
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

The UnlicenseGNU Affero General Public License v3.0 or laterGNU General Public License v2.0 or laterBSD 4-Clause \u201cOriginal\u201d or \u201cOld\u201d LicenseMIT LicenseApache License 2.0 Text Only
This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <https://unlicense.org>\n
Text Only
                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<https://www.gnu.org/licenses/>.\n
Text Only
                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n
Text Only
BSD 4-Clause License\n\nCopyright (c) 2022-Present, CHANfiG Contributors\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n3. All advertising materials mentioning features or use of this software must\n   display the following acknowledgement:\n     This product includes software developed by [project].\n\n4. Neither the name of the copyright holder nor the names of its\n   contributors may be used to endorse or promote products derived from\n   this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER \"AS IS\" AND ANY EXPRESS OR\nIMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\nEVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\nOR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\nWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\nOTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\nADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n
Text Only
MIT License\n\nCopyright (c) 2022-Present, CHANfiG Contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n
Text Only
                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n

\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

"},{"location":"zh/about/","title":"\u5173\u4e8e","text":"

\u200b\u7531\u4e39\u7075\u200b\u5728\u200b\u5730\u7403\u200b\u5f00\u53d1\u200b

\u200b\u6211\u4eec\u200b\u662f\u200b\u4e00\u4e2a\u200b\u7531\u200b\u5f00\u53d1\u8005\u200b\u3001\u200b\u8bbe\u8ba1\u200b\u4eba\u5458\u200b\u548c\u200b\u5176\u4ed6\u200b\u4eba\u5458\u200b\u7ec4\u6210\u200b\u7684\u200b\u793e\u533a\u200b\uff0c\u200b\u81f4\u529b\u4e8e\u200b\u8ba9\u200b\u6df1\u5ea6\u200b\u5b66\u4e60\u200b\u6280\u672f\u200b\u66f4\u52a0\u200b\u5f00\u653e\u200b\u3002

\u200b\u6211\u4eec\u200b\u662f\u200b\u4e00\u4e2a\u200b\u7531\u200b\u4e2a\u4f53\u200b\u7ec4\u6210\u200b\u7684\u200b\u793e\u533a\u200b\uff0c\u200b\u81f4\u529b\u4e8e\u200b\u63a8\u52a8\u200b\u6df1\u5ea6\u200b\u5b66\u4e60\u200b\u7684\u200b\u53ef\u80fd\u6027\u200b\u8fb9\u754c\u200b\u3002

\u200b\u6211\u4eec\u200b\u5bf9\u200b\u6df1\u5ea6\u200b\u5b66\u4e60\u200b\u53ca\u5176\u200b\u7528\u6237\u200b\u5145\u6ee1\u200b\u6fc0\u60c5\u200b\u3002

\u200b\u6211\u4eec\u200b\u662f\u200b\u4e39\u7075\u200b\u3002

"},{"location":"zh/about/license/","title":"License","text":"

\u200b\u7ffb\u8bd1\u200b

\u200b\u672c\u6587\u200b\u5185\u5bb9\u200b\u4e3a\u200b\u7ffb\u8bd1\u200b\u7248\u672c\u200b\uff0c\u200b\u65e8\u5728\u200b\u4e3a\u200b\u7528\u6237\u200b\u63d0\u4f9b\u65b9\u4fbf\u200b\u3002 \u200b\u6211\u4eec\u200b\u5df2\u7ecf\u200b\u5c3d\u529b\u200b\u786e\u4fdd\u200b\u7ffb\u8bd1\u200b\u7684\u200b\u51c6\u786e\u6027\u200b\u3002 \u200b\u4f46\u200b\u8bf7\u200b\u6ce8\u610f\u200b\uff0c\u200b\u7ffb\u8bd1\u200b\u5185\u5bb9\u200b\u53ef\u80fd\u200b\u5305\u542b\u200b\u9519\u8bef\u200b\uff0c\u200b\u4ec5\u4f9b\u53c2\u8003\u200b\u3002 \u200b\u8bf7\u4ee5\u200b\u82f1\u6587\u200b\u539f\u6587\u200b\u4e3a\u51c6\u200b\u3002

\u200b\u4e3a\u200b\u6ee1\u8db3\u200b\u5408\u89c4\u6027\u200b\u4e0e\u200b\u6267\u6cd5\u200b\u8981\u6c42\u200b\uff0c\u200b\u7ffb\u8bd1\u200b\u6587\u6863\u200b\u4e2d\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e0d\u200b\u51c6\u786e\u200b\u6216\u200b\u6b67\u4e49\u200b\u4e4b\u5904\u200b\u5747\u200b\u4e0d\u200b\u5177\u6709\u200b\u7ea6\u675f\u529b\u200b\uff0c\u200b\u4e5f\u200b\u4e0d\u200b\u5177\u5907\u200b\u6cd5\u5f8b\u6548\u529b\u200b\u3002

"},{"location":"zh/about/license/#gnu-affero","title":"GNU AFFERO \u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1","text":"

\u200b\u7b2c\u200b3\u200b\u7248\u200b\uff0c2007\u200b\u5e74\u200b11\u200b\u6708\u200b19\u200b\u65e5\u200b

\u200b\u7248\u6743\u6240\u6709\u200b \u00a9 2007 Free Software Foundation, Inc. https://fsf.org/

Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

\u200b\u6bcf\u4e2a\u200b\u4eba\u200b\u90fd\u200b\u88ab\u200b\u5141\u8bb8\u200b\u590d\u5236\u200b\u548c\u200b\u5206\u53d1\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6587\u4ef6\u200b\u7684\u200b\u9010\u5b57\u200b\u526f\u672c\u200b\uff0c\u200b\u4f46\u200b\u4e0d\u200b\u5141\u8bb8\u200b\u8fdb\u884c\u200b\u66f4\u6539\u200b\u3002

"},{"location":"zh/about/license/#_1","title":"\u5e8f\u8a00","text":"

GNU Affero \u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u662f\u200b\u4e00\u4e2a\u200b\u81ea\u7531\u200b\u7684\u200b\u3001\u200b\u5141\u8bb8\u200b\u590d\u5236\u200b\u7684\u200b\u8f6f\u4ef6\u200b\u548c\u200b\u5176\u4ed6\u200b\u7c7b\u578b\u200b\u4f5c\u54c1\u200b\u7684\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u5728\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u8f6f\u4ef6\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u5b83\u200b\u662f\u200b\u4e13\u95e8\u200b\u4e3a\u200b\u786e\u4fdd\u200b\u4e0e\u200b\u793e\u533a\u200b\u5408\u4f5c\u200b\u800c\u200b\u8bbe\u8ba1\u200b\u3002

\u200b\u5927\u591a\u6570\u200b\u8f6f\u4ef6\u200b\u548c\u200b\u5176\u4ed6\u200b\u5b9e\u7528\u200b\u4f5c\u54c1\u200b\u7684\u200b\u8bb8\u53ef\u200b\u90fd\u200b\u662f\u200b\u4e3a\u4e86\u200b\u5265\u593a\u200b\u60a8\u200b\u5206\u4eab\u200b\u548c\u200b\u6539\u53d8\u200b\u4f5c\u54c1\u200b\u7684\u200b\u81ea\u7531\u200b\u3002\u200b\u76f8\u6bd4\u4e4b\u4e0b\u200b\uff0c\u200b\u6211\u4eec\u200b\u7684\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u76ee\u7684\u200b\u662f\u200b\u4fdd\u8bc1\u200b\u60a8\u200b\u5206\u4eab\u200b\u548c\u200b\u6539\u53d8\u200b\u4e00\u4e2a\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u6240\u6709\u200b\u7248\u672c\u200b\u7684\u200b\u81ea\u7531\u200b\u2013\u200b\u786e\u4fdd\u200b\u5b83\u200b\u5bf9\u200b\u6240\u6709\u200b\u7528\u6237\u200b\u90fd\u200b\u662f\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u3002

\u200b\u5f53\u200b\u6211\u4eec\u200b\u8c08\u8bba\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u65f6\u200b\uff0c\u200b\u6211\u4eec\u200b\u6307\u200b\u7684\u200b\u662f\u200b\u81ea\u7531\u200b\uff0c\u200b\u800c\u200b\u4e0d\u662f\u200b\u4ef7\u683c\u200b\u3002\u200b\u6211\u4eec\u200b\u7684\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u8bbe\u8ba1\u200b\u662f\u200b\u4e3a\u4e86\u200b\u786e\u4fdd\u60a8\u200b\u6709\u200b\u5206\u53d1\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u526f\u672c\u200b\u7684\u200b\u81ea\u7531\u200b\uff08\u200b\u5982\u679c\u200b\u60a8\u200b\u613f\u610f\u200b\uff0c\u200b\u8fd8\u200b\u53ef\u4ee5\u200b\u6536\u8d39\u200b\uff09\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u6536\u5230\u200b\u6e90\u4ee3\u7801\u200b\uff0c\u200b\u6216\u8005\u200b\u5982\u679c\u200b\u60a8\u200b\u60f3\u5f97\u5230\u200b\u5b83\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u6539\u53d8\u200b\u8f6f\u4ef6\u200b\u6216\u200b\u5728\u200b\u65b0\u200b\u7684\u200b\u81ea\u7531\u200b\u7a0b\u5e8f\u200b\u4e2d\u200b\u4f7f\u7528\u200b\u5b83\u200b\u7684\u200b\u7247\u6bb5\u200b\uff0c\u200b\u800c\u4e14\u200b\u60a8\u200b\u77e5\u9053\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u505a\u200b\u8fd9\u4e9b\u200b\u4e8b\u60c5\u200b\u3002

\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u5f00\u53d1\u8005\u200b\u901a\u8fc7\u200b\u4e24\u4e2a\u200b\u6b65\u9aa4\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b\u3002(1)\u200b\u4e3b\u5f20\u200b\u8f6f\u4ef6\u200b\u7684\u200b\u7248\u6743\u200b\uff0c(2)\u200b\u5411\u200b\u60a8\u200b\u63d0\u4f9b\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u5141\u8bb8\u200b\u60a8\u200b\u5408\u6cd5\u200b\u5730\u200b\u590d\u5236\u200b\u3001\u200b\u5206\u53d1\u200b\u548c\u200b/\u200b\u6216\u200b\u4fee\u6539\u200b\u8be5\u8f6f\u4ef6\u200b\u3002

\u200b\u634d\u536b\u200b\u6240\u6709\u200b\u7528\u6237\u200b\u81ea\u7531\u200b\u7684\u200b\u4e00\u4e2a\u200b\u6b21\u8981\u200b\u597d\u5904\u200b\u662f\u200b\uff0c\u200b\u5982\u679c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u66ff\u4ee3\u200b\u7248\u672c\u200b\u5f97\u5230\u200b\u5e7f\u6cdb\u200b\u4f7f\u7528\u200b\uff0c\u200b\u5c31\u200b\u53ef\u4ee5\u200b\u4f9b\u200b\u5176\u4ed6\u200b\u5f00\u53d1\u8005\u200b\u4f7f\u7528\u200b\u3002\u200b\u8bb8\u591a\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u7684\u200b\u5f00\u53d1\u8005\u200b\u5bf9\u200b\u7531\u6b64\u200b\u4ea7\u751f\u200b\u7684\u200b\u5408\u4f5c\u200b\u611f\u5230\u200b\u632f\u594b\u200b\u548c\u200b\u9f13\u821e\u200b\u3002\u200b\u7136\u800c\u200b\uff0c\u200b\u5728\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\u4f7f\u7528\u200b\u7684\u200b\u8f6f\u4ef6\u200b\uff0c\u200b\u8fd9\u79cd\u200b\u7ed3\u679c\u200b\u53ef\u80fd\u200b\u65e0\u6cd5\u200b\u5b9e\u73b0\u200b\u3002GNU\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u5141\u8bb8\u200b\u5236\u4f5c\u200b\u4e00\u4e2a\u200b\u4fee\u6539\u200b\u8fc7\u200b\u7684\u200b\u7248\u672c\u200b\uff0c\u200b\u8ba9\u200b\u516c\u4f17\u200b\u5728\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\u8bbf\u95ee\u200b\u5b83\u200b\uff0c\u200b\u800c\u200b\u4e0d\u200b\u9700\u8981\u200b\u5411\u200b\u516c\u4f17\u200b\u53d1\u5e03\u200b\u5176\u200b\u6e90\u4ee3\u7801\u200b\u3002

GNU Affero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u662f\u200b\u4e13\u95e8\u200b\u8bbe\u8ba1\u200b\u6765\u200b\u786e\u4fdd\u200b\u5728\u200b\u8fd9\u79cd\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u4fee\u6539\u200b\u540e\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u53ef\u4ee5\u200b\u88ab\u200b\u793e\u533a\u200b\u4f7f\u7528\u200b\u3002\u200b\u5b83\u200b\u8981\u6c42\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u200b\u7684\u200b\u8fd0\u8425\u5546\u200b\u5411\u200b\u8be5\u200b\u670d\u52a1\u5668\u200b\u7684\u200b\u7528\u6237\u200b\u63d0\u4f9b\u200b\u8fd0\u884c\u200b\u5728\u200b\u90a3\u91cc\u200b\u7684\u200b\u4fee\u6539\u200b\u7248\u672c\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u3002\u200b\u56e0\u6b64\u200b\uff0c\u200b\u5728\u200b\u4e00\u4e2a\u200b\u53ef\u200b\u516c\u5f00\u200b\u8bbf\u95ee\u200b\u7684\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\u516c\u5f00\u200b\u4f7f\u7528\u200b\u4e00\u4e2a\u200b\u4fee\u6539\u200b\u8fc7\u200b\u7684\u200b\u7248\u672c\u200b\uff0c\u200b\u4f7f\u200b\u516c\u4f17\u200b\u80fd\u591f\u200b\u83b7\u5f97\u200b\u4fee\u6539\u200b\u8fc7\u200b\u7684\u200b\u7248\u672c\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u3002

\u200b\u4e00\u4e2a\u200b\u8f83\u200b\u65e9\u200b\u7684\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u79f0\u4e3a\u200bAffero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u7531\u200bAffero\u200b\u53d1\u5e03\u200b\uff0c\u200b\u65e8\u5728\u200b\u5b9e\u73b0\u200b\u7c7b\u4f3c\u200b\u76ee\u6807\u200b\u3002\u200b\u8fd9\u662f\u200b\u4e00\u4e2a\u200b\u4e0d\u540c\u200b\u7684\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u4e0d\u662f\u200bAffero GPL\u200b\u7684\u200b\u4e00\u4e2a\u200b\u7248\u672c\u200b\uff0c\u200b\u4f46\u200bAffero\u200b\u5df2\u7ecf\u200b\u53d1\u5e03\u200b\u4e86\u200bAffero GPL\u200b\u7684\u200b\u4e00\u4e2a\u200b\u65b0\u200b\u7248\u672c\u200b\uff0c\u200b\u5141\u8bb8\u200b\u5728\u200b\u8fd9\u4e2a\u200b\u8bb8\u53ef\u8bc1\u200b\u4e0b\u200b\u91cd\u65b0\u200b\u8bb8\u53ef\u200b\u3002

\u200b\u5173\u4e8e\u200b\u590d\u5236\u200b\u3001\u200b\u5206\u53d1\u200b\u548c\u200b\u4fee\u6539\u200b\u7684\u200b\u786e\u5207\u200b\u6761\u6b3e\u200b\u548c\u200b\u6761\u4ef6\u200b\u5982\u4e0b\u200b\u3002

"},{"location":"zh/about/license/#_2","title":"\u6761\u6b3e\u200b\u4e0e\u200b\u6761\u4ef6","text":""},{"location":"zh/about/license/#0","title":"0. \u200b\u5b9a\u4e49\u200b.","text":"

\u201c\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u201d \u200b\u662f\u200b\u6307\u200bGNU Affero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u7b2c\u4e09\u7248\u200b\u3002 \u201c\u200b\u7248\u6743\u200b\u201d \u200b\u4e5f\u200b\u6307\u200b\u9002\u7528\u200b\u4e8e\u200b\u5176\u4ed6\u200b\u7c7b\u578b\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7c7b\u4f3c\u200b\u7248\u6743\u200b\u7684\u200b\u6cd5\u5f8b\u200b\uff0c\u200b\u5982\u200b\u534a\u5bfc\u4f53\u200b\u63a9\u6a21\u200b\u3002

\u201c\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u5728\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e0b\u200b\u8bb8\u53ef\u200b\u7684\u200b\u4efb\u4f55\u200b\u6709\u200b\u7248\u6743\u200b\u7684\u200b\u4f5c\u54c1\u200b\u3002\u200b\u6bcf\u4e2a\u200b\u88ab\u200b\u8bb8\u53ef\u200b\u4eba\u200b\u90fd\u200b\u88ab\u200b\u79f0\u547c\u200b\u4e3a\u200b \u201c\u200b\u60a8\u200b\u201d\u3002\u201d\u200b\u88ab\u200b\u8bb8\u53ef\u200b\u4eba\u200b\u201d \u200b\u548c\u200b \u201c\u200b\u63a5\u53d7\u8005\u200b\u201d \u200b\u53ef\u4ee5\u200b\u662f\u200b\u4e2a\u4eba\u200b\u6216\u200b\u7ec4\u7ec7\u200b\u3002

\u201c\u200b\u4fee\u6539\u200b\u201d \u200b\u4f5c\u54c1\u200b\u662f\u200b\u6307\u4ee5\u200b\u9700\u8981\u200b\u7248\u6743\u200b\u8bb8\u53ef\u200b\u7684\u200b\u65b9\u5f0f\u200b\u590d\u5236\u200b\u6216\u200b\u6539\u7f16\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u7684\u200b\u5168\u90e8\u200b\u6216\u200b\u90e8\u5206\u200b\u5185\u5bb9\u200b\uff0c\u200b\u800c\u200b\u4e0d\u662f\u200b\u5236\u4f5c\u200b\u4e00\u4e2a\u200b\u5b8c\u5168\u200b\u7684\u200b\u526f\u672c\u200b\u3002\u200b\u7531\u6b64\u200b\u4ea7\u751f\u200b\u7684\u200b\u4f5c\u54c1\u200b\u88ab\u200b\u79f0\u4e3a\u200b\u65e9\u671f\u200b\u4f5c\u54c1\u200b\u7684\u200b \u201c\u200b\u4fee\u6539\u7248\u200b\u201d \u200b\u6216\u200b \u201c\u200b\u57fa\u4e8e\u200b\u201d \u200b\u65e9\u671f\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4f5c\u54c1\u200b\u3002 \u200b\u4e00\u4e2a\u200b \u201c\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u672a\u7ecf\u200b\u4fee\u6539\u200b\u7684\u200b\u7a0b\u5e8f\u200b\u6216\u200b\u57fa\u4e8e\u200b\u8be5\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u4f5c\u54c1\u200b\u3002

\u201c\u200b\u4f20\u64ad\u200b\u201d \u200b\u4f5c\u54c1\u200b\u662f\u200b\u6307\u200b\u5728\u200b\u672a\u7ecf\u8bb8\u53ef\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u5bf9\u200b\u4f5c\u54c1\u200b\u505a\u200b\u4efb\u4f55\u200b\u4e8b\u60c5\u200b\uff0c\u200b\u4f7f\u200b\u60a8\u200b\u5728\u200b\u9002\u7528\u200b\u7684\u200b\u7248\u6743\u6cd5\u200b\u4e0b\u200b\u627f\u62c5\u200b\u76f4\u63a5\u200b\u6216\u200b\u95f4\u63a5\u200b\u7684\u200b\u4fb5\u6743\u200b\u8d23\u4efb\u200b\uff0c\u200b\u4f46\u200b\u5728\u200b\u8ba1\u7b97\u673a\u200b\u4e0a\u200b\u6267\u884c\u200b\u6216\u200b\u4fee\u6539\u200b\u79c1\u4eba\u200b\u526f\u672c\u200b\u9664\u5916\u200b\u3002\u200b\u4f20\u64ad\u200b\u5305\u62ec\u200b\u590d\u5236\u200b\u3001\u200b\u5206\u53d1\u200b\uff08\u200b\u65e0\u8bba\u200b\u662f\u5426\u200b\u4fee\u6539\u200b\uff09\u3001\u200b\u5411\u200b\u516c\u4f17\u200b\u63d0\u4f9b\u200b\uff0c\u200b\u5728\u200b\u4e00\u4e9b\u200b\u56fd\u5bb6\u200b\u8fd8\u200b\u5305\u62ec\u200b\u5176\u4ed6\u200b\u6d3b\u52a8\u200b\u3002 \u200b\u4f20\u64ad\u200b\u201d \u200b\u4f5c\u54c1\u200b\u662f\u200b\u6307\u4f7f\u200b\u5176\u4ed6\u200b\u5404\u65b9\u200b\u80fd\u591f\u200b\u5236\u4f5c\u200b\u6216\u200b\u63a5\u53d7\u200b\u526f\u672c\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e00\u79cd\u200b\u4f20\u64ad\u200b\u3002\u200b\u4ec5\u4ec5\u200b\u662f\u200b\u901a\u8fc7\u200b\u8ba1\u7b97\u673a\u7f51\u7edc\u200b\u4e0e\u200b\u7528\u6237\u200b\u4e92\u52a8\u200b\uff0c\u200b\u800c\u200b\u6ca1\u6709\u200b\u8f6c\u8ba9\u200b\u526f\u672c\u200b\uff0c\u200b\u5e76\u200b\u4e0d\u662f\u200b\u4f20\u64ad\u200b\u3002

\u200b\u4ea4\u4e92\u5f0f\u200b\u7528\u6237\u754c\u9762\u200b\u663e\u793a\u200b \u201c\u200b\u9002\u5f53\u200b\u7684\u200b\u6cd5\u5f8b\u200b\u58f0\u660e\u200b\u201d \u200b\u7684\u200b\u7a0b\u5ea6\u200b\u662f\u200b\uff0c\u200b\u5b83\u200b\u5305\u62ec\u200b\u4e00\u4e2a\u200b\u65b9\u4fbf\u200b\u548c\u200b\u663e\u773c\u200b\u7684\u200b\u529f\u80fd\u200b\uff0c(1)\u200b\u663e\u793a\u200b\u9002\u5f53\u200b\u7684\u200b\u7248\u6743\u200b\u58f0\u660e\u200b\uff0c(2)\u200b\u544a\u8bc9\u200b\u7528\u6237\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u6ca1\u6709\u200b\u4fdd\u8bc1\u200b\uff08\u200b\u9664\u4e86\u200b\u63d0\u4f9b\u200b\u4fdd\u8bc1\u200b\u7684\u200b\u8303\u56f4\u200b\uff09\uff0c\u200b\u88ab\u200b\u8bb8\u53ef\u200b\u4eba\u200b\u53ef\u4ee5\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u200b\u4f20\u8fbe\u200b\u8be5\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u5982\u4f55\u200b\u67e5\u770b\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u526f\u672c\u200b\u3002\u200b\u5982\u679c\u200b\u754c\u9762\u200b\u5448\u73b0\u200b\u7684\u200b\u662f\u200b\u4e00\u4e2a\u200b\u7528\u6237\u200b\u547d\u4ee4\u200b\u6216\u200b\u9009\u9879\u200b\u7684\u200b\u5217\u8868\u200b\uff0c\u200b\u5982\u200b\u83dc\u5355\u200b\uff0c\u200b\u90a3\u4e48\u200b\u5217\u8868\u200b\u4e2d\u200b\u7684\u200b\u7a81\u51fa\u200b\u9879\u76ee\u200b\u5c31\u200b\u7b26\u5408\u200b\u8fd9\u4e00\u200b\u6807\u51c6\u200b\u3002

"},{"location":"zh/about/license/#1","title":"1. \u200b\u6e90\u4ee3\u7801\u200b.","text":"

\u200b\u4f5c\u54c1\u200b\u7684\u200b \u201c\u200b\u6e90\u4ee3\u7801\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u5bf9\u200b\u4f5c\u54c1\u200b\u8fdb\u884c\u200b\u4fee\u6539\u200b\u7684\u200b\u9996\u9009\u200b\u5f62\u5f0f\u200b\u3002\u201d\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4efb\u4f55\u200b\u975e\u200b\u6e90\u7801\u200b\u5f62\u5f0f\u200b\u3002

\u201c\u200b\u6807\u51c6\u63a5\u53e3\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u7531\u200b\u516c\u8ba4\u200b\u7684\u200b\u6807\u51c6\u200b\u673a\u6784\u200b\u5b9a\u4e49\u200b\u7684\u200b\u5b98\u65b9\u200b\u6807\u51c6\u200b\u7684\u200b\u63a5\u53e3\u200b\uff0c\u200b\u6216\u8005\u200b\u5728\u200b\u4e3a\u200b\u67d0\u200b\u4e00\u200b\u7279\u5b9a\u200b\u7f16\u7a0b\u8bed\u8a00\u200b\u6307\u5b9a\u200b\u63a5\u53e3\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u6307\u5728\u200b\u4f7f\u7528\u200b\u8be5\u200b\u8bed\u8a00\u200b\u7684\u200b\u5f00\u53d1\u8005\u200b\u4e2d\u200b\u5e7f\u6cdb\u200b\u4f7f\u7528\u200b\u7684\u200b\u63a5\u53e3\u200b\u3002

\u200b\u53ef\u200b\u6267\u884c\u200b\u4f5c\u54c1\u200b\u7684\u200b \u201c\u200b\u7cfb\u7edf\u200b\u5e93\u200b\u201d \u200b\u5305\u62ec\u200b\u9664\u200b\u4f5c\u54c1\u200b\u6574\u4f53\u200b\u4ee5\u5916\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e1c\u897f\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u4e1c\u897f\u200b(a)\u200b\u4ee5\u200b\u6b63\u5e38\u200b\u7684\u200b\u5f62\u5f0f\u200b\u6253\u5305\u200b\u4e00\u4e2a\u200b\u4e3b\u8981\u200b\u90e8\u4ef6\u200b\uff0c\u200b\u4f46\u200b\u4e0d\u662f\u200b\u8be5\u200b\u4e3b\u8981\u200b\u90e8\u4ef6\u200b\u7684\u200b\u4e00\u90e8\u5206\u200b\uff0c\u200b\u4ee5\u53ca\u200b(b)\u200b\u4ec5\u200b\u7528\u4e8e\u200b\u4f7f\u200b\u4f5c\u54c1\u200b\u4e0e\u200b\u8be5\u200b\u4e3b\u8981\u200b\u90e8\u4ef6\u200b\u4e00\u8d77\u200b\u4f7f\u7528\u200b\uff0c\u200b\u6216\u200b\u7528\u4e8e\u200b\u5b9e\u73b0\u200b\u4e00\u4e2a\u200b\u6807\u51c6\u63a5\u53e3\u200b\uff0c\u200b\u8be5\u200b\u63a5\u53e3\u200b\u7684\u200b\u5b9e\u73b0\u200b\u5df2\u4ee5\u200b\u6e90\u4ee3\u7801\u200b\u5f62\u5f0f\u200b\u5411\u200b\u516c\u4f17\u200b\u63d0\u4f9b\u200b\u3002\u200b\u8fd9\u91cc\u200b\u7684\u200b \u201c\u200b\u4e3b\u8981\u200b\u90e8\u4ef6\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u53ef\u200b\u6267\u884c\u200b\u4f5c\u54c1\u200b\u6240\u200b\u8fd0\u884c\u200b\u7684\u200b\u7279\u5b9a\u200b\u64cd\u4f5c\u7cfb\u7edf\u200b\uff08\u200b\u5982\u679c\u200b\u6709\u200b\u7684\u8bdd\u200b\uff09\u200b\u7684\u200b\u4e3b\u8981\u200b\u57fa\u672c\u200b\u90e8\u4ef6\u200b\uff08\u200b\u5185\u6838\u200b\u3001\u200b\u7a97\u53e3\u200b\u7cfb\u7edf\u200b\u7b49\u200b\uff09\uff0c\u200b\u6216\u200b\u7528\u4e8e\u200b\u5236\u4f5c\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7f16\u8bd1\u5668\u200b\uff0c\u200b\u6216\u200b\u7528\u4e8e\u200b\u8fd0\u884c\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u7684\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u89e3\u91ca\u5668\u200b\u3002

\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u5f62\u5f0f\u200b\u7684\u200b\u4f5c\u54c1\u200b\u7684\u200b \u201c\u200b\u76f8\u5e94\u200b\u6e90\u4ee3\u7801\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u751f\u6210\u200b\u3001\u200b\u5b89\u88c5\u200b\u548c\u200b\uff08\u200b\u5bf9\u4e8e\u200b\u53ef\u200b\u6267\u884c\u200b\u4f5c\u54c1\u200b\uff09\u200b\u8fd0\u884c\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u4ee5\u53ca\u200b\u4fee\u6539\u200b\u4f5c\u54c1\u200b\u6240\u200b\u9700\u200b\u7684\u200b\u6240\u6709\u200b\u6e90\u4ee3\u7801\u200b\uff0c\u200b\u5305\u62ec\u200b\u63a7\u5236\u200b\u8fd9\u4e9b\u200b\u6d3b\u52a8\u200b\u7684\u200b\u811a\u672c\u200b\u3002\u200b\u4f46\u662f\u200b\uff0c\u200b\u5b83\u200b\u4e0d\u200b\u5305\u62ec\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7cfb\u7edf\u200b\u5e93\u200b\uff0c\u200b\u4e5f\u200b\u4e0d\u200b\u5305\u62ec\u200b\u5728\u200b\u6267\u884c\u200b\u8fd9\u4e9b\u200b\u6d3b\u52a8\u200b\u65f6\u200b\u672a\u7ecf\u200b\u4fee\u6539\u200b\u4f46\u200b\u4e0d\u200b\u5c5e\u4e8e\u200b\u4f5c\u54c1\u200b\u7684\u200b\u901a\u7528\u200b\u5de5\u5177\u200b\u6216\u200b\u666e\u904d\u200b\u53ef\u7528\u200b\u7684\u200b\u514d\u8d39\u200b\u7a0b\u5e8f\u200b\u3002\u200b\u4f8b\u5982\u200b\uff0c\u200b\u76f8\u5e94\u200b\u6e90\u200b\u5305\u62ec\u200b\u4e0e\u200b\u4f5c\u54c1\u200b\u7684\u200b\u6e90\u6587\u4ef6\u200b\u76f8\u5173\u200b\u7684\u200b\u63a5\u53e3\u5b9a\u4e49\u200b\u6587\u4ef6\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u4f5c\u54c1\u200b\u4e13\u95e8\u200b\u8bbe\u8ba1\u200b\u7684\u200b\u5171\u4eab\u200b\u5e93\u200b\u548c\u200b\u52a8\u6001\u200b\u94fe\u63a5\u200b\u7684\u200b\u5b50\u7a0b\u5e8f\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\uff0c\u200b\u4f8b\u5982\u200b\u901a\u8fc7\u200b\u4eb2\u5bc6\u200b\u7684\u200b\u6570\u636e\u901a\u4fe1\u200b\u6216\u200b\u63a7\u5236\u6d41\u200b\u5728\u200b\u8fd9\u4e9b\u200b\u5b50\u7a0b\u5e8f\u200b\u548c\u200b\u4f5c\u54c1\u200b\u7684\u200b\u5176\u4ed6\u200b\u90e8\u5206\u200b\u4e4b\u95f4\u200b\u3002

\u200b\u76f8\u5e94\u200b\u6e90\u200b\u4e0d\u200b\u9700\u8981\u200b\u5305\u62ec\u200b\u7528\u6237\u200b\u53ef\u4ee5\u200b\u4ece\u200b\u76f8\u5e94\u200b\u6e90\u200b\u7684\u200b\u5176\u4ed6\u200b\u90e8\u5206\u200b\u81ea\u52a8\u200b\u91cd\u65b0\u200b\u751f\u6210\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e1c\u897f\u200b\u3002

\u200b\u6e90\u4ee3\u7801\u200b\u5f62\u5f0f\u200b\u7684\u200b\u4f5c\u54c1\u200b\u7684\u200b\u76f8\u5e94\u200b\u6e90\u200b\u662f\u200b\u6307\u200b\u540c\u4e00\u200b\u4f5c\u54c1\u200b\u3002

"},{"location":"zh/about/license/#2","title":"2. \u200b\u57fa\u672c\u200b\u6743\u9650\u200b.","text":"

\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6388\u4e88\u200b\u7684\u200b\u6240\u6709\u200b\u6743\u5229\u200b\u90fd\u200b\u662f\u200b\u5728\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u7248\u6743\u200b\u671f\u9650\u5185\u200b\u6388\u4e88\u200b\u7684\u200b\uff0c\u200b\u5e76\u4e14\u200b\u5728\u200b\u6ee1\u8db3\u200b\u6240\u8ff0\u200b\u6761\u4ef6\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u662f\u200b\u4e0d\u53ef\u200b\u64a4\u6d88\u200b\u7684\u200b\u3002\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u660e\u786e\u200b\u80af\u5b9a\u200b\u4e86\u200b\u60a8\u200b\u5bf9\u200b\u8fd0\u884c\u200b\u672a\u7ecf\u200b\u4fee\u6539\u200b\u7684\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u65e0\u9650\u200b\u8bb8\u53ef\u200b\u3002\u200b\u53ea\u6709\u200b\u5728\u200b\u8f93\u51fa\u200b\u7684\u200b\u5185\u5bb9\u200b\u6784\u6210\u200b\u4e86\u200b\u4e00\u4e2a\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u8fd0\u884c\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u8f93\u51fa\u200b\u624d\u200b\u53d7\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u4fdd\u62a4\u200b\u3002\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u627f\u8ba4\u200b\u60a8\u200b\u7684\u200b\u5408\u7406\u200b\u4f7f\u7528\u6743\u200b\u6216\u200b\u7248\u6743\u6cd5\u200b\u6240\u200b\u89c4\u5b9a\u200b\u7684\u200b\u5176\u4ed6\u200b\u540c\u7b49\u200b\u6743\u5229\u200b\u3002

\u200b\u53ea\u8981\u200b\u60a8\u200b\u7684\u200b\u8bb8\u53ef\u8bc1\u200b\u4ecd\u7136\u200b\u6709\u6548\u200b\uff0c\u200b\u53ef\u4ee5\u200b\u65e0\u6761\u4ef6\u200b\u5730\u200b\u5236\u4f5c\u200b\u3001\u200b\u8fd0\u884c\u200b\u548c\u200b\u4f20\u64ad\u200b\u60a8\u200b\u6ca1\u6709\u200b\u8f6c\u8fbe\u200b\u7684\u200b\u6db5\u76d6\u200b\u4f5c\u54c1\u200b\u3002\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u5c06\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u4f20\u8fbe\u200b\u7ed9\u200b\u5176\u4ed6\u4eba\u200b\uff0c\u200b\u552f\u4e00\u200b\u7684\u200b\u76ee\u7684\u200b\u662f\u200b\u8ba9\u200b\u4ed6\u4eec\u200b\u4e13\u95e8\u200b\u4e3a\u200b\u60a8\u200b\u8fdb\u884c\u200b\u4fee\u6539\u200b\uff0c\u200b\u6216\u200b\u4e3a\u200b\u60a8\u200b\u63d0\u4f9b\u200b\u8fd0\u884c\u200b\u8fd9\u4e9b\u200b\u4f5c\u54c1\u200b\u7684\u200b\u8bbe\u65bd\u200b\uff0c\u200b\u524d\u63d0\u200b\u662f\u200b\u60a8\u200b\u5728\u200b\u4f20\u8fbe\u200b\u6240\u6709\u200b\u60a8\u200b\u4e0d\u200b\u63a7\u5236\u200b\u7248\u6743\u200b\u7684\u200b\u6750\u6599\u200b\u65f6\u200b\u9075\u5b88\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u6761\u6b3e\u200b\u3002\u200b\u90a3\u4e9b\u200b\u4e3a\u200b\u60a8\u200b\u5236\u4f5c\u200b\u6216\u200b\u8fd0\u884c\u200b\u6240\u6d89\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4eba\u200b\u5fc5\u987b\u200b\u5b8c\u5168\u200b\u4ee3\u8868\u200b\u60a8\u200b\uff0c\u200b\u5728\u200b\u60a8\u200b\u7684\u200b\u6307\u5bfc\u200b\u548c\u200b\u63a7\u5236\u200b\u4e0b\u200b\uff0c\u200b\u6309\u7167\u200b\u7981\u6b62\u200b\u4ed6\u4eec\u200b\u5728\u200b\u4e0e\u200b\u60a8\u200b\u7684\u200b\u5173\u7cfb\u200b\u4e4b\u5916\u200b\u5236\u4f5c\u200b\u60a8\u200b\u7684\u200b\u7248\u6743\u200b\u6750\u6599\u200b\u7684\u200b\u4efb\u4f55\u200b\u526f\u672c\u200b\u7684\u200b\u6761\u6b3e\u200b\u6765\u200b\u8fdb\u884c\u200b\u3002

\u200b\u5728\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u4ec5\u200b\u5728\u200b\u4e0b\u8ff0\u200b\u6761\u4ef6\u200b\u4e0b\u200b\u5141\u8bb8\u200b\u8f6c\u8ba9\u200b\u3002\u200b\u4e0d\u200b\u5141\u8bb8\u200b\u8f6c\u200b\u6388\u6743\u200b\uff1b\u200b\u7b2c\u200b10\u200b\u6761\u200b\u89c4\u5b9a\u200b\u6ca1\u6709\u200b\u5fc5\u8981\u200b\u3002

"},{"location":"zh/about/license/#3","title":"3. \u200b\u4ece\u200b\u53cd\u200b\u89c4\u907f\u200b\u6cd5\u4e2d\u200b\u4fdd\u62a4\u200b\u7528\u6237\u200b\u7684\u200b\u5408\u6cd5\u6743\u5229\u200b.","text":"

\u200b\u6839\u636e\u200b\u4efb\u4f55\u200b\u5c65\u884c\u200b1996\u200b\u5e74\u200b12\u200b\u6708\u200b20\u200b\u65e5\u200b\u901a\u8fc7\u200b\u7684\u200b\u4e16\u754c\u77e5\u8bc6\u4ea7\u6743\u7ec4\u7ec7\u200b\u7248\u6743\u200b\u6761\u7ea6\u200b\u7b2c\u200b11\u200b\u6761\u200b\u89c4\u5b9a\u200b\u7684\u200b\u4e49\u52a1\u200b\u7684\u200b\u9002\u7528\u6cd5\u5f8b\u200b\uff0c\u200b\u6216\u200b\u7981\u6b62\u200b\u6216\u200b\u9650\u5236\u200b\u89c4\u907f\u200b\u6b64\u7c7b\u200b\u63aa\u65bd\u200b\u7684\u200b\u7c7b\u4f3c\u200b\u6cd5\u5f8b\u200b\uff0c\u200b\u4efb\u4f55\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u90fd\u200b\u4e0d\u5f97\u200b\u88ab\u200b\u89c6\u4e3a\u200b\u6709\u6548\u200b\u6280\u672f\u200b\u63aa\u65bd\u200b\u7684\u200b\u4e00\u90e8\u5206\u200b\u3002

\u200b\u5f53\u200b\u60a8\u200b\u4f20\u8fbe\u200b\u4e00\u4e2a\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u65f6\u200b\uff0c\u200b\u60a8\u200b\u653e\u5f03\u200b\u4efb\u4f55\u200b\u7981\u6b62\u200b\u89c4\u907f\u200b\u6280\u672f\u200b\u63aa\u65bd\u200b\u7684\u200b\u6cd5\u5f8b\u200b\u6743\u529b\u200b\uff0c\u200b\u53ea\u8981\u200b\u8fd9\u79cd\u200b\u89c4\u907f\u200b\u662f\u200b\u901a\u8fc7\u200b\u5bf9\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u884c\u4f7f\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e0b\u200b\u7684\u200b\u6743\u5229\u200b\u800c\u200b\u5b9e\u73b0\u200b\u7684\u200b\uff0c\u200b\u5e76\u4e14\u200b\u60a8\u200b\u5426\u8ba4\u200b\u6709\u200b\u4efb\u4f55\u200b\u9650\u5236\u200b\u64cd\u4f5c\u200b\u6216\u200b\u4fee\u6539\u200b\u4f5c\u54c1\u200b\u7684\u200b\u610f\u56fe\u200b\uff0c\u200b\u4ee5\u200b\u4f5c\u4e3a\u200b\u5bf9\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7528\u6237\u200b\u5f3a\u5236\u6267\u884c\u200b\u60a8\u200b\u6216\u200b\u7b2c\u4e09\u65b9\u200b\u7981\u6b62\u200b\u89c4\u907f\u200b\u6280\u672f\u200b\u63aa\u65bd\u200b\u7684\u200b\u6cd5\u5f8b\u200b\u6743\u5229\u200b\u7684\u200b\u624b\u6bb5\u200b\u3002

"},{"location":"zh/about/license/#4","title":"4. \u200b\u4f20\u9012\u200b\u9010\u5b57\u200b\u62f7\u8d1d\u200b.","text":"

\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u5728\u200b\u6536\u5230\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u540e\u200b\uff0c\u200b\u4ee5\u200b\u4efb\u4f55\u200b\u5a92\u4ecb\u200b\u4f20\u9012\u200b\u5176\u200b\u9010\u5b57\u200b\u62f7\u8d1d\u200b\uff0c\u200b\u4f46\u200b\u60a8\u200b\u5fc5\u987b\u200b\u5728\u200b\u6bcf\u4efd\u200b\u62f7\u8d1d\u200b\u4e0a\u200b\u9192\u76ee\u200b\u5730\u200b\u3001\u200b\u9002\u5f53\u200b\u5730\u200b\u53d1\u5e03\u200b\u9002\u5f53\u200b\u7684\u200b\u7248\u6743\u200b\u58f0\u660e\u200b\uff1b\u200b\u4fdd\u6301\u200b\u6240\u6709\u200b\u8bf4\u660e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u548c\u200b\u6839\u636e\u200b\u7b2c\u200b7\u200b\u6761\u200b\u6dfb\u52a0\u200b\u7684\u200b\u4efb\u4f55\u200b\u975e\u200b\u8bb8\u53ef\u200b\u6761\u6b3e\u200b\u9002\u7528\u200b\u4e8e\u200b\u4ee3\u7801\u200b\u7684\u200b\u58f0\u660e\u200b\u5b8c\u6574\u65e0\u7f3a\u200b\uff1b\u200b\u4fdd\u6301\u200b\u6240\u6709\u200b\u5173\u4e8e\u200b\u6ca1\u6709\u200b\u4efb\u4f55\u200b\u4fdd\u8bc1\u200b\u7684\u200b\u58f0\u660e\u200b\u5b8c\u6574\u65e0\u7f3a\u200b\uff1b\u200b\u5e76\u200b\u5c06\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u62f7\u8d1d\u200b\u4e0e\u200b\u7a0b\u5e8f\u200b\u4e00\u8d77\u200b\u4ea4\u7ed9\u200b\u6240\u6709\u200b\u63a5\u6536\u8005\u200b\u3002

\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u5bf9\u200b\u6bcf\u4efd\u200b\u62f7\u8d1d\u200b\u6536\u53d6\u200b\u4efb\u4f55\u200b\u8d39\u7528\u200b\uff0c\u200b\u4e5f\u200b\u53ef\u4ee5\u200b\u4e0d\u200b\u6536\u53d6\u200b\u4efb\u4f55\u200b\u8d39\u7528\u200b\uff0c\u200b\u60a8\u200b\u8fd8\u200b\u53ef\u4ee5\u200b\u63d0\u4f9b\u200b\u6709\u507f\u200b\u7684\u200b\u652f\u6301\u200b\u6216\u200b\u4fdd\u4fee\u200b\u4fdd\u62a4\u200b\u3002

"},{"location":"zh/about/license/#5","title":"5. \u200b\u4f20\u9012\u200b\u4fee\u6539\u200b\u540e\u200b\u7684\u200b\u6e90\u200b\u7248\u672c\u200b.","text":"

\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u6839\u636e\u200b\u7b2c\u200b 4 \u200b\u8282\u200b\u7684\u200b\u6761\u6b3e\u200b\uff0c\u200b\u4ee5\u200b\u6e90\u4ee3\u7801\u200b\u7684\u200b\u5f62\u5f0f\u200b\u4f20\u8fbe\u200b\u57fa\u4e8e\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u6216\u200b\u6839\u636e\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u4fee\u6539\u200b\u800c\u200b\u4ea7\u751f\u200b\u7684\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u4f46\u200b\u60a8\u200b\u5fc5\u987b\u200b\u6ee1\u8db3\u200b\u4ee5\u4e0b\u200b\u6240\u6709\u200b\u6761\u4ef6\u200b:

a) \u200b\u4f5c\u54c1\u200b\u5fc5\u987b\u200b\u6709\u200b\u9192\u76ee\u200b\u7684\u200b\u58f0\u660e\u200b\uff0c\u200b\u8bf4\u660e\u200b\u60a8\u200b\u4fee\u6539\u200b\u4e86\u200b\u5b83\u200b\uff0c\u200b\u5e76\u200b\u7ed9\u51fa\u200b\u76f8\u5173\u200b\u7684\u200b\u65e5\u671f\u200b\u3002 b) \u200b\u4f5c\u54c1\u200b\u5fc5\u987b\u200b\u6709\u200b\u9192\u76ee\u200b\u7684\u200b\u58f0\u660e\u200b\uff0c\u200b\u8bf4\u660e\u200b\u5b83\u200b\u662f\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u548c\u200b\u6839\u636e\u200b\u7b2c\u200b7\u200b\u6761\u200b\u589e\u52a0\u200b\u7684\u200b\u6761\u4ef6\u200b\u53d1\u5e03\u200b\u7684\u200b\u3002\u200b\u8fd9\u4e00\u200b\u8981\u6c42\u200b\u4fee\u6539\u200b\u4e86\u200b\u7b2c\u200b4\u200b\u8282\u4e2d\u200b \u201c\u200b\u4fdd\u6301\u200b\u6240\u6709\u200b\u901a\u77e5\u200b\u7684\u200b\u5b8c\u6574\u6027\u200b\u201d \u200b\u7684\u200b\u8981\u6c42\u200b\u3002 c) \u200b\u60a8\u200b\u5fc5\u987b\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u5c06\u200b\u6574\u4e2a\u200b\u4f5c\u54c1\u200b\u4f5c\u4e3a\u200b\u4e00\u4e2a\u200b\u6574\u4f53\u200b\u8bb8\u53ef\u200b\u7ed9\u200b\u4efb\u4f55\u200b\u62e5\u6709\u200b\u5176\u200b\u526f\u672c\u200b\u7684\u200b\u4eba\u200b\u3002\u200b\u56e0\u6b64\u200b\uff0c\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u5c06\u200b\u4e0e\u200b\u4efb\u4f55\u200b\u9002\u7528\u200b\u7684\u200b\u7b2c\u200b7\u200b\u6761\u200b\u9644\u52a0\u200b\u6761\u6b3e\u200b\u4e00\u8d77\u200b\uff0c\u200b\u9002\u7528\u200b\u4e8e\u200b\u6574\u4e2a\u200b\u4f5c\u54c1\u200b\u53ca\u5176\u200b\u6240\u6709\u200b\u90e8\u5206\u200b\uff0c\u200b\u65e0\u8bba\u200b\u5b83\u4eec\u200b\u662f\u200b\u5982\u4f55\u200b\u5305\u88c5\u200b\u7684\u200b\u3002\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e0d\u200b\u5141\u8bb8\u200b\u4ee5\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u65b9\u5f0f\u200b\u8bb8\u53ef\u200b\u8be5\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u4f46\u200b\u5982\u679c\u200b\u60a8\u200b\u5df2\u7ecf\u200b\u5355\u72ec\u200b\u6536\u5230\u200b\u4e86\u200b\u8fd9\u79cd\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u5b83\u200b\u4e5f\u200b\u4e0d\u4f1a\u200b\u4f7f\u200b\u8fd9\u79cd\u200b\u8bb8\u53ef\u200b\u5931\u6548\u200b\u3002 \u200b\u5982\u679c\u200b\u4e00\u4e2a\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u4e0e\u200b\u5176\u4ed6\u200b\u5355\u72ec\u200b\u548c\u200b\u72ec\u7acb\u200b\u7684\u200b\u4f5c\u54c1\u200b\u7684\u200b\u6c47\u7f16\u200b\uff0c\u200b\u5176\u200b\u6027\u8d28\u200b\u4e0d\u662f\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u5ef6\u4f38\u200b\uff0c\u200b\u5e76\u4e14\u200b\u6ca1\u6709\u200b\u4e0e\u200b\u4e4b\u200b\u7ed3\u5408\u200b\u4ee5\u200b\u5f62\u6210\u200b\u66f4\u5927\u200b\u7684\u200b\u7a0b\u5e8f\u200b\uff0c\u200b\u5728\u200b\u5b58\u50a8\u200b\u6216\u200b\u5206\u53d1\u200b\u5a92\u4ecb\u200b\u7684\u200b\u67d0\u200b\u4e00\u5377\u200b\u4e0a\u200b\uff0c\u200b\u5982\u679c\u200b\u8be5\u200b\u6c47\u7f16\u200b\u53ca\u5176\u200b\u4ea7\u751f\u200b\u7684\u200b\u7248\u6743\u200b\u6ca1\u6709\u200b\u88ab\u200b\u7528\u6765\u200b\u9650\u5236\u200b\u6c47\u7f16\u200b\u7528\u6237\u200b\u7684\u200b\u8bbf\u95ee\u200b\u6216\u200b\u6cd5\u5f8b\u200b\u6743\u5229\u200b\uff0c\u200b\u8d85\u51fa\u200b\u5355\u4e2a\u200b\u4f5c\u54c1\u200b\u5141\u8bb8\u200b\u7684\u200b\u8303\u56f4\u200b\uff0c\u200b\u5219\u200b\u88ab\u200b\u79f0\u4e3a\u200b \u201c\u200b\u805a\u5408\u200b\u201d\u3002\u200b\u5c06\u200b\u4e00\u4e2a\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\u5305\u542b\u200b\u5728\u200b\u4e00\u4e2a\u200b\u603b\u4f53\u200b\u4e2d\u200b\u5e76\u200b\u4e0d\u200b\u5bfc\u81f4\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u9002\u7528\u200b\u4e8e\u200b\u603b\u4f53\u200b\u7684\u200b\u5176\u4ed6\u200b\u90e8\u5206\u200b\u3002

"},{"location":"zh/about/license/#6","title":"6. \u200b\u4f20\u9012\u200b\u975e\u6e90\u200b\u5f62\u5f0f\u200b.","text":"

\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u6839\u636e\u200b\u7b2c\u200b4\u200b\u6761\u200b\u548c\u200b\u7b2c\u200b5\u200b\u6761\u200b\u7684\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u4ee5\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u5f62\u5f0f\u200b\u4f20\u9012\u200b\u88ab\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u4f46\u200b\u60a8\u200b\u4e5f\u200b\u5fc5\u987b\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u4ee5\u200b\u4e0b\u5217\u200b\u65b9\u5f0f\u200b\u4e4b\u4e00\u200b\u4f20\u9012\u200b\u673a\u5668\u200b\u53ef\u8bfb\u200b\u7684\u200b\u76f8\u5e94\u200b\u6e90\u4ee3\u7801\u200b:

a) \u200b\u5728\u200b\u5b9e\u4f53\u200b\u4ea7\u54c1\u200b\uff08\u200b\u5305\u62ec\u200b\u5b9e\u4f53\u200b\u9500\u552e\u200b\u5a92\u4ecb\u200b\uff09\u200b\u4e2d\u200b\u4f20\u9012\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\uff0c\u200b\u6216\u200b\u4f53\u73b0\u200b\u5728\u200b\u5b9e\u4f53\u200b\u4ea7\u54c1\u200b\uff08\u200b\u5305\u62ec\u200b\u5b9e\u4f53\u200b\u9500\u552e\u200b\u5a92\u4ecb\u200b\uff09\u200b\u4e2d\u200b\uff0c\u200b\u540c\u65f6\u200b\u5c06\u200b\u76f8\u5e94\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u56fa\u5b9a\u200b\u5728\u200b\u901a\u5e38\u200b\u7528\u4e8e\u200b\u8f6f\u4ef6\u200b\u4ea4\u6362\u200b\u7684\u200b\u8010\u7528\u200b\u5b9e\u4f53\u200b\u5a92\u4ecb\u200b\u4e0a\u200b\u3002 b) \u200b\u5728\u200b\u5b9e\u7269\u200b\u4ea7\u54c1\u200b\uff08\u200b\u5305\u62ec\u200b\u5b9e\u7269\u200b\u9500\u552e\u200b\u5a92\u4ecb\u200b\uff09\u200b\u4e2d\u200b\u4f20\u9012\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\uff0c\u200b\u6216\u200b\u5728\u200b\u5b9e\u7269\u200b\u4ea7\u54c1\u200b\uff08\u200b\u5305\u62ec\u200b\u5b9e\u7269\u200b\u9500\u552e\u200b\u5a92\u4ecb\u200b\uff09\u200b\u4e2d\u200b\u4f53\u73b0\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\uff0c\u200b\u5e76\u200b\u9644\u6709\u200b\u4e00\u4efd\u200b\u81f3\u5c11\u200b\u4e09\u5e74\u200b\u6709\u6548\u200b\u7684\u200b\u4e66\u9762\u200b\u62a5\u4ef7\u200b\uff0c\u200b\u53ea\u8981\u200b\u60a8\u200b\u4e3a\u200b\u8be5\u200b\u4ea7\u54c1\u578b\u53f7\u200b\u63d0\u4f9b\u200b\u5907\u4ef6\u200b\u6216\u200b\u5ba2\u6237\u200b\u652f\u6301\u200b\uff0c\u200b\u5c31\u200b\u4e00\u76f4\u200b\u6709\u6548\u200b\u3002\u200b\u5411\u200b\u4efb\u4f55\u200b\u62e5\u6709\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u4eba\u200b\u63d0\u4f9b\u200b(1)\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6240\u200b\u6db5\u76d6\u200b\u7684\u200b\u4ea7\u54c1\u200b\u4e2d\u200b\u6240\u6709\u200b\u8f6f\u4ef6\u200b\u7684\u200b\u76f8\u5e94\u200b\u6e90\u4ee3\u7801\u200b\u7684\u200b\u62f7\u8d1d\u200b\uff0c\u200b\u62f7\u8d1d\u200b\u5728\u200b\u901a\u5e38\u200b\u7528\u4e8e\u200b\u8f6f\u4ef6\u200b\u4ea4\u6362\u200b\u7684\u200b\u8010\u7528\u200b\u7269\u7406\u4ecb\u8d28\u200b\u4e0a\u200b\uff0c\u200b\u5176\u200b\u4ef7\u683c\u200b\u4e0d\u200b\u8d85\u8fc7\u200b\u8d35\u65b9\u200b\u5b9e\u9645\u200b\u6267\u884c\u200b\u8fd9\u4e00\u200b\u4f20\u9012\u200b\u6e90\u4ee3\u7801\u200b\u7684\u200b\u5408\u7406\u200b\u6210\u672c\u200b\uff0c\u200b\u6216\u8005\u200b(2)\u200b\u4ece\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\u514d\u8d39\u200b\u83b7\u53d6\u200b\u76f8\u5e94\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u62f7\u8d1d\u200b\u3002 c) \u200b\u5c06\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u5355\u4e2a\u200b\u526f\u672c\u200b\u4e0e\u200b\u63d0\u4f9b\u200b\u76f8\u5e94\u200b\u6765\u6e90\u200b\u7684\u200b\u4e66\u9762\u200b\u63d0\u8bae\u200b\u7684\u200b\u526f\u672c\u200b\u4e00\u8d77\u200b\u4f20\u9001\u200b\u3002\u200b\u53ea\u6709\u200b\u5728\u200b\u5076\u5c14\u200b\u548c\u200b\u975e\u5546\u4e1a\u6027\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u5e76\u4e14\u200b\u53ea\u6709\u200b\u5728\u200b\u60a8\u200b\u6536\u5230\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u548c\u200b\u8fd9\u79cd\u200b\u63d0\u8bae\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u624d\u200b\u5141\u8bb8\u200b\u8fd9\u79cd\u200b\u9009\u62e9\u200b\uff0c\u200b\u7b26\u5408\u200b\u7b2c\u200b6b\u200b\u6b3e\u200b\u7684\u200b\u89c4\u5b9a\u200b\u3002 d) \u200b\u901a\u8fc7\u200b\u63d0\u4f9b\u200b\u4ece\u200b\u6307\u5b9a\u200b\u5730\u70b9\u200b\uff08\u200b\u514d\u8d39\u200b\u6216\u200b\u6536\u8d39\u200b\uff09\u200b\u83b7\u53d6\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\uff0c\u200b\u5e76\u200b\u4ee5\u200b\u540c\u6837\u200b\u7684\u200b\u65b9\u5f0f\u200b\u901a\u8fc7\u200b\u540c\u4e00\u200b\u5730\u70b9\u200b\u63d0\u4f9b\u200b\u76f8\u5e94\u200b\u7684\u200b\u6e90\u7801\u200b\uff0c\u200b\u800c\u200b\u4e0d\u518d\u200b\u6536\u8d39\u200b\u3002\u200b\u60a8\u200b\u4e0d\u200b\u9700\u8981\u200b\u8981\u6c42\u200b\u63a5\u53d7\u8005\u200b\u5728\u200b\u590d\u5236\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u540c\u65f6\u200b\u590d\u5236\u200b\u76f8\u5e94\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u3002\u200b\u5982\u679c\u200b\u590d\u5236\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u5730\u65b9\u200b\u662f\u200b\u4e00\u4e2a\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u200b\uff0c\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u53ef\u4ee5\u200b\u5728\u200b\u53e6\u200b\u4e00\u4e2a\u200b\u652f\u6301\u200b\u540c\u7b49\u200b\u590d\u5236\u200b\u8bbe\u65bd\u200b\u7684\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\uff08\u200b\u7531\u200b\u60a8\u200b\u6216\u200b\u7b2c\u4e09\u65b9\u200b\u8fd0\u8425\u200b\uff09\uff0c\u200b\u53ea\u8981\u200b\u60a8\u200b\u5728\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u65c1\u8fb9\u200b\u4fdd\u6301\u200b\u660e\u786e\u200b\u7684\u200b\u6307\u793a\u200b\uff0c\u200b\u8bf4\u660e\u200b\u5728\u200b\u54ea\u91cc\u200b\u53ef\u4ee5\u200b\u627e\u5230\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u3002\u200b\u65e0\u8bba\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u5728\u200b\u54ea\u4e2a\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\uff0c\u200b\u60a8\u200b\u90fd\u200b\u6709\u200b\u4e49\u52a1\u200b\u786e\u4fdd\u200b\u5728\u200b\u6ee1\u8db3\u200b\u8fd9\u4e9b\u200b\u8981\u6c42\u200b\u6240\u200b\u9700\u200b\u7684\u200b\u65f6\u95f4\u200b\u5185\u200b\u63d0\u4f9b\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u3002 e) \u200b\u4f7f\u7528\u200b\u70b9\u5bf9\u70b9\u200b\u4f20\u8f93\u200b\u7684\u200b\u65b9\u5f0f\u200b\u4f20\u9001\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\uff0c\u200b\u4f46\u200b\u60a8\u200b\u5fc5\u987b\u200b\u544a\u77e5\u200b\u5176\u4ed6\u200b\u540c\u884c\u200b\uff0c\u200b\u6839\u636e\u200b\u7b2c\u200b6d\u200b\u6b3e\u200b\uff0c\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u548c\u200b\u4f5c\u54c1\u200b\u7684\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u6b63\u5728\u200b\u514d\u8d39\u200b\u63d0\u4f9b\u200b\u7ed9\u200b\u516c\u4f17\u200b\u3002 \u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u53ef\u200b\u5206\u79bb\u200b\u90e8\u5206\u200b\uff0c\u200b\u5176\u200b\u6e90\u4ee3\u7801\u200b\u4f5c\u4e3a\u200b\u7cfb\u7edf\u200b\u5e93\u200b\u88ab\u200b\u6392\u9664\u200b\u5728\u200b\u76f8\u5e94\u200b\u7684\u200b\u6e90\u7801\u200b\u4e4b\u5916\u200b\uff0c\u200b\u4e0d\u200b\u9700\u8981\u200b\u5305\u62ec\u200b\u5728\u200b\u4f20\u8fbe\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u4f5c\u54c1\u200b\u4e2d\u200b\u3002

\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b \u201c\u200b\u662f\u200b\u6307\u200b(1) \u201c\u200b\u6d88\u8d39\u54c1\u200b\u201d\uff0c\u200b\u5373\u200b\u901a\u5e38\u200b\u7528\u4e8e\u200b\u4e2a\u4eba\u200b\u3001\u200b\u5bb6\u5ead\u200b\u6216\u200b\u5bb6\u5c45\u200b\u7528\u9014\u200b\u7684\u200b\u4efb\u4f55\u200b\u6709\u5f62\u200b\u4e2a\u4eba\u8d22\u4ea7\u200b\uff0c\u200b\u6216\u200b(2)\u200b\u4e3a\u200b\u7eb3\u5165\u200b\u4f4f\u5b85\u200b\u800c\u200b\u8bbe\u8ba1\u200b\u6216\u200b\u51fa\u552e\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e1c\u897f\u200b\u3002\u200b\u5728\u200b\u786e\u5b9a\u200b\u4e00\u4e2a\u200b\u4ea7\u54c1\u200b\u662f\u5426\u662f\u200b\u6d88\u8d39\u54c1\u200b\u65f6\u200b\uff0c\u200b\u6709\u200b\u7591\u95ee\u200b\u7684\u200b\u60c5\u51b5\u200b\u5e94\u200b\u4ee5\u200b\u6709\u5229\u4e8e\u200b\u627f\u4fdd\u200b\u7684\u200b\u65b9\u5f0f\u200b\u89e3\u51b3\u200b\u3002\u200b\u5bf9\u4e8e\u200b\u7279\u5b9a\u200b\u7528\u6237\u200b\u6536\u5230\u200b\u7684\u200b\u7279\u5b9a\u200b\u4ea7\u54c1\u200b\uff0c\u201d\u200b\u901a\u5e38\u200b\u4f7f\u7528\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u8be5\u7c7b\u200b\u4ea7\u54c1\u200b\u7684\u200b\u5178\u578b\u200b\u6216\u200b\u5e38\u89c1\u200b\u7528\u9014\u200b\uff0c\u200b\u800c\u200b\u4e0d\u200b\u8003\u8651\u200b\u7279\u5b9a\u200b\u7528\u6237\u200b\u7684\u200b\u5730\u4f4d\u200b\u6216\u200b\u7279\u5b9a\u200b\u7528\u6237\u200b\u5b9e\u9645\u200b\u4f7f\u7528\u200b\u6216\u200b\u671f\u671b\u200b\u6216\u200b\u9884\u671f\u200b\u4f7f\u7528\u200b\u8be5\u200b\u4ea7\u54c1\u200b\u7684\u200b\u65b9\u5f0f\u200b\u3002\u200b\u4e00\u4e2a\u200b\u4ea7\u54c1\u200b\u662f\u200b\u6d88\u8d39\u7c7b\u200b\u4ea7\u54c1\u200b\uff0c\u200b\u65e0\u8bba\u200b\u8be5\u200b\u4ea7\u54c1\u200b\u662f\u5426\u200b\u6709\u200b\u5927\u91cf\u200b\u7684\u200b\u5546\u4e1a\u200b\u3001\u200b\u5de5\u4e1a\u200b\u6216\u975e\u200b\u6d88\u8d39\u7c7b\u200b\u7528\u9014\u200b\uff0c\u200b\u9664\u975e\u200b\u8fd9\u4e9b\u200b\u7528\u9014\u200b\u662f\u200b\u8be5\u200b\u4ea7\u54c1\u200b\u7684\u200b\u552f\u4e00\u200b\u91cd\u8981\u200b\u4f7f\u7528\u200b\u65b9\u5f0f\u200b\u3002

\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u7684\u200b \u201c\u200b\u5b89\u88c5\u200b\u4fe1\u606f\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u5728\u200b\u8be5\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u4e2d\u200b\u4ece\u200b\u5176\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u7684\u200b\u4fee\u6539\u200b\u7248\u672c\u200b\u5b89\u88c5\u200b\u548c\u200b\u6267\u884c\u200b\u6240\u200b\u6db5\u76d6\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4fee\u6539\u200b\u7248\u672c\u200b\u6240\u200b\u9700\u200b\u7684\u200b\u4efb\u4f55\u200b\u65b9\u6cd5\u200b\u3001\u200b\u7a0b\u5e8f\u200b\u3001\u200b\u6388\u6743\u200b\u5bc6\u94a5\u200b\u6216\u200b\u5176\u4ed6\u200b\u4fe1\u606f\u200b\u3002\u200b\u8fd9\u4e9b\u200b\u4fe1\u606f\u200b\u5fc5\u987b\u200b\u8db3\u4ee5\u200b\u786e\u4fdd\u200b\u5728\u200b\u4efb\u4f55\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u90fd\u200b\u4e0d\u4f1a\u200b\u4ec5\u4ec5\u200b\u56e0\u4e3a\u200b\u8fdb\u884c\u200b\u4e86\u200b\u4fee\u6539\u200b\u800c\u200b\u963b\u6b62\u200b\u6216\u200b\u5e72\u6270\u200b\u4fee\u6539\u200b\u540e\u200b\u7684\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u7ee7\u7eed\u200b\u8fd0\u884c\u200b\u3002

\u200b\u5982\u679c\u200b\u60a8\u200b\u5728\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u4e2d\u200b\uff0c\u200b\u6216\u200b\u4e0e\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u4e00\u8d77\u200b\uff0c\u200b\u6216\u200b\u4e13\u95e8\u200b\u5728\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u4e2d\u200b\u4f7f\u7528\u200b\uff0c\u200b\u5e76\u200b\u4f5c\u4e3a\u200b\u4ea4\u6613\u200b\u7684\u200b\u4e00\u90e8\u5206\u200b\uff0c\u200b\u5c06\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u7684\u200b\u62e5\u6709\u6743\u200b\u548c\u200b\u4f7f\u7528\u6743\u200b\u6c38\u4e45\u200b\u6216\u200b\u56fa\u5b9a\u200b\u5730\u200b\u8f6c\u8ba9\u200b\u7ed9\u200b\u63a5\u53d7\u8005\u200b\uff08\u200b\u65e0\u8bba\u200b\u4ea4\u6613\u200b\u5982\u4f55\u200b\u5b9a\u6027\u200b\uff09\uff0c\u200b\u6839\u636e\u200b\u672c\u6761\u200b\u89c4\u5b9a\u200b\u8f6c\u8ba9\u200b\u7684\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u5fc5\u987b\u200b\u9644\u6709\u200b\u5b89\u88c5\u200b\u4fe1\u606f\u200b\u3002\u200b\u4f46\u662f\u200b\uff0c\u200b\u5982\u679c\u200b\u60a8\u200b\u6216\u200b\u4efb\u4f55\u200b\u7b2c\u4e09\u65b9\u200b\u90fd\u200b\u6ca1\u6709\u200b\u4fdd\u7559\u200b\u5728\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u4e0a\u200b\u5b89\u88c5\u200b\u4fee\u6539\u200b\u8fc7\u200b\u7684\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u80fd\u529b\u200b\uff08\u200b\u4f8b\u5982\u200b\uff0c\u200b\u4f5c\u54c1\u200b\u5df2\u7ecf\u200b\u5b89\u88c5\u200b\u5728\u200bROM\u200b\u4e2d\u200b\uff09\uff0c\u200b\u5219\u200b\u8be5\u200b\u8981\u6c42\u200b\u4e0d\u200b\u9002\u7528\u200b\u3002

\u200b\u63d0\u4f9b\u200b\u5b89\u88c5\u200b\u4fe1\u606f\u200b\u7684\u200b\u8981\u6c42\u200b\u4e0d\u200b\u5305\u62ec\u200b\u7ee7\u7eed\u200b\u4e3a\u200b\u88ab\u200b\u63a5\u53d7\u8005\u200b\u4fee\u6539\u200b\u6216\u200b\u5b89\u88c5\u200b\u7684\u200b\u4f5c\u54c1\u200b\u6216\u200b\u88ab\u200b\u4fee\u6539\u200b\u6216\u200b\u5b89\u88c5\u200b\u7684\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u63d0\u4f9b\u200b\u652f\u6301\u200b\u670d\u52a1\u200b\u3001\u200b\u4fdd\u8bc1\u200b\u6216\u200b\u66f4\u65b0\u200b\u7684\u200b\u8981\u6c42\u200b\u3002\u200b\u5f53\u200b\u4fee\u6539\u200b\u672c\u8eab\u200b\u5bf9\u200b\u7f51\u7edc\u200b\u7684\u200b\u8fd0\u884c\u200b\u4ea7\u751f\u200b\u5b9e\u8d28\u6027\u200b\u7684\u200b\u4e0d\u5229\u200b\u5f71\u54cd\u200b\u6216\u200b\u8fdd\u53cd\u200b\u4e86\u200b\u7f51\u7edc\u200b\u4e0a\u200b\u7684\u200b\u901a\u4fe1\u200b\u89c4\u5219\u200b\u548c\u200b\u534f\u8bae\u200b\u65f6\u200b\uff0c\u200b\u53ef\u4ee5\u200b\u62d2\u7edd\u200b\u8bbf\u95ee\u200b\u7f51\u7edc\u200b\u3002 \u200b\u6839\u636e\u200b\u672c\u200b\u8282\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u6240\u200b\u4f20\u8fbe\u200b\u7684\u200b\u76f8\u5e94\u200b\u6e90\u7801\u200b\u548c\u200b\u6240\u200b\u63d0\u4f9b\u200b\u7684\u200b\u5b89\u88c5\u200b\u4fe1\u606f\u200b\u5fc5\u987b\u200b\u662f\u200b\u516c\u5f00\u200b\u8bb0\u5f55\u200b\u7684\u200b\u683c\u5f0f\u200b\uff08\u200b\u5e76\u200b\u4ee5\u200b\u6e90\u4ee3\u7801\u200b\u7684\u200b\u5f62\u5f0f\u200b\u5411\u200b\u516c\u4f17\u200b\u63d0\u4f9b\u200b\u5b9e\u73b0\u200b\uff09\uff0c\u200b\u5e76\u4e14\u200b\u5fc5\u987b\u200b\u4e0d\u200b\u9700\u8981\u200b\u7279\u6b8a\u200b\u7684\u200b\u5bc6\u7801\u200b\u6216\u200b\u94a5\u5319\u200b\u6765\u200b\u89e3\u5305\u200b\u3001\u200b\u9605\u8bfb\u200b\u6216\u200b\u590d\u5236\u200b\u3002

"},{"location":"zh/about/license/#7","title":"7. \u200b\u9644\u52a0\u200b\u6761\u6b3e\u200b.","text":"

\u201c\u200b\u9644\u52a0\u200b\u8bb8\u53ef\u200b\u201d \u200b\u662f\u200b\u5bf9\u200b\u672c\u200b\u8bb8\u53ef\u200b\u6761\u6b3e\u200b\u7684\u200b\u8865\u5145\u200b\uff0c\u200b\u5bf9\u200b\u5176\u4e2d\u200b\u7684\u200b\u4e00\u4e2a\u200b\u6216\u200b\u591a\u4e2a\u200b\u6761\u4ef6\u200b\u4f5c\u51fa\u200b\u4f8b\u5916\u200b\u89c4\u5b9a\u200b\u3002\u200b\u9002\u7528\u200b\u4e8e\u200b\u6574\u4e2a\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u9644\u52a0\u200b\u8bb8\u53ef\u200b\u5e94\u200b\u88ab\u200b\u89c6\u4e3a\u200b\u5305\u62ec\u200b\u5728\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e2d\u200b\uff0c\u200b\u53ea\u8981\u200b\u5b83\u4eec\u200b\u5728\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u4e0b\u200b\u6709\u6548\u200b\u3002\u200b\u5982\u679c\u200b\u9644\u52a0\u200b\u8bb8\u53ef\u200b\u53ea\u200b\u9002\u7528\u200b\u4e8e\u672c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u4e00\u90e8\u5206\u200b\uff0c\u200b\u5219\u200b\u8be5\u200b\u90e8\u5206\u200b\u53ef\u4ee5\u200b\u6839\u636e\u200b\u8fd9\u4e9b\u200b\u8bb8\u53ef\u200b\u5355\u72ec\u200b\u4f7f\u7528\u200b\uff0c\u200b\u4f46\u200b\u6574\u4e2a\u200b\u7a0b\u5e8f\u200b\u4ecd\u200b\u53d7\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u7ba1\u8f96\u200b\uff0c\u200b\u800c\u200b\u4e0d\u200b\u8003\u8651\u200b\u9644\u52a0\u200b\u8bb8\u53ef\u200b\u3002

\u200b\u5f53\u200b\u60a8\u200b\u8f6c\u9001\u200b\u4e00\u4efd\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u526f\u672c\u200b\u65f6\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u9009\u62e9\u200b\u4ece\u200b\u8be5\u200b\u526f\u672c\u200b\u6216\u200b\u5176\u200b\u4efb\u4f55\u200b\u90e8\u5206\u200b\u4e2d\u200b\u5220\u9664\u200b\u4efb\u4f55\u200b\u9644\u52a0\u200b\u8bb8\u53ef\u200b\u3002(\u200b\u5728\u200b\u67d0\u4e9b\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u5f53\u200b\u60a8\u200b\u4fee\u6539\u200b\u4f5c\u54c1\u200b\u65f6\u200b\uff0c\u200b\u9644\u52a0\u200b\u8bb8\u53ef\u200b\u53ef\u80fd\u200b\u88ab\u200b\u5199\u6210\u200b\u9700\u8981\u200b\u81ea\u5df1\u200b\u5220\u9664\u200b)\u3002\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u5728\u200b\u60a8\u200b\u6dfb\u52a0\u200b\u5230\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u4e2d\u200b\u7684\u200b\u6750\u6599\u200b\u4e0a\u200b\u653e\u7f6e\u200b\u989d\u5916\u200b\u7684\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u5bf9\u4e8e\u200b\u8fd9\u4e9b\u200b\u6750\u6599\u200b\uff0c\u200b\u60a8\u200b\u6709\u200b\u6216\u200b\u53ef\u4ee5\u200b\u7ed9\u4e88\u200b\u9002\u5f53\u200b\u7684\u200b\u7248\u6743\u200b\u8bb8\u53ef\u200b\u3002

\u200b\u5c3d\u7ba1\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6709\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u5bf9\u4e8e\u200b\u60a8\u200b\u6dfb\u52a0\u200b\u5230\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u4e2d\u200b\u7684\u200b\u6750\u6599\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\uff08\u200b\u5982\u679c\u200b\u5f97\u5230\u200b\u8be5\u200b\u6750\u6599\u200b\u7684\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u7684\u200b\u6388\u6743\u200b\uff09\u200b\u7528\u200b\u4ee5\u4e0b\u200b\u6761\u6b3e\u200b\u8865\u5145\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u6761\u6b3e\u200b:

a) \u200b\u4ee5\u200b\u4e0d\u540c\u4e8e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7b2c\u200b15\u200b\u6761\u200b\u548c\u200b\u7b2c\u200b16\u200b\u6761\u200b\u7684\u200b\u6761\u6b3e\u200b\u58f0\u660e\u200b\u4fdd\u8bc1\u200b\u6216\u200b\u9650\u5236\u200b\u8d23\u4efb\u200b\uff1b\u200b\u6216\u200b b) \u200b\u8981\u6c42\u200b\u5728\u200b\u8be5\u200b\u6750\u6599\u200b\u6216\u200b\u5305\u542b\u200b\u8be5\u200b\u6750\u6599\u200b\u7684\u200b\u4f5c\u54c1\u200b\u6240\u200b\u663e\u793a\u200b\u7684\u200b\u9002\u5f53\u200b\u6cd5\u5f8b\u200b\u58f0\u660e\u200b\u4e2d\u200b\u4fdd\u7559\u200b\u7279\u5b9a\u200b\u7684\u200b\u5408\u7406\u200b\u6cd5\u5f8b\u200b\u58f0\u660e\u200b\u6216\u200b\u4f5c\u8005\u200b\u5f52\u5c5e\u200b\uff1b\u200b\u6216\u200b c) \u200b\u7981\u6b62\u200b\u6b6a\u66f2\u200b\u8be5\u200b\u6750\u6599\u200b\u7684\u200b\u6765\u6e90\u200b\uff0c\u200b\u6216\u200b\u8981\u6c42\u200b\u4ee5\u200b\u5408\u7406\u200b\u7684\u200b\u65b9\u5f0f\u200b\u5c06\u200b\u8be5\u200b\u6750\u6599\u200b\u7684\u200b\u4fee\u6539\u200b\u7248\u672c\u200b\u6807\u8bb0\u200b\u4e3a\u200b\u4e0e\u200b\u539f\u59cb\u200b\u7248\u672c\u200b\u4e0d\u540c\u200b\uff1b\u200b\u6216\u200b d) \u200b\u9650\u5236\u200b\u4e3a\u200b\u5ba3\u4f20\u200b\u76ee\u7684\u200b\u4f7f\u7528\u200b\u8be5\u200b\u6750\u6599\u200b\u7684\u200b\u8bb8\u53ef\u200b\u4eba\u200b\u6216\u200b\u4f5c\u8005\u200b\u7684\u200b\u59d3\u540d\u200b\uff1b\u200b\u6216\u200b e) \u200b\u62d2\u7edd\u200b\u6839\u636e\u200b\u5546\u6807\u6cd5\u200b\u6388\u4e88\u200b\u4f7f\u7528\u200b\u67d0\u4e9b\u200b\u5546\u53f7\u200b\u3001\u200b\u5546\u6807\u200b\u6216\u200b\u670d\u52a1\u200b\u6807\u5fd7\u200b\u7684\u200b\u6743\u5229\u200b\uff1b\u200b\u6216\u200b f) \u200b\u8981\u6c42\u200b\u5c06\u200b\u6750\u6599\u200b\uff08\u200b\u6216\u200b\u6750\u6599\u200b\u7684\u200b\u4fee\u6539\u200b\u7248\u672c\u200b\uff09\u200b\u8f6c\u200b\u4ea4\u7ed9\u200b\u63a5\u53d7\u8005\u200b\u7684\u200b\u4efb\u4f55\u4eba\u200b\u5bf9\u200b\u8fd9\u4e9b\u200b\u5408\u540c\u200b\u5047\u8bbe\u200b\u76f4\u63a5\u200b\u52a0\u200b\u5728\u200b\u8fd9\u4e9b\u200b\u8bb8\u53ef\u200b\u4eba\u200b\u548c\u200b\u4f5c\u8005\u200b\u8eab\u4e0a\u200b\u7684\u200b\u4efb\u4f55\u200b\u8d23\u4efb\u200b\u8fdb\u884c\u200b\u8d54\u507f\u200b\u3002 \u200b\u6240\u6709\u200b\u5176\u4ed6\u200b\u975e\u200b\u8bb8\u53ef\u200b\u6027\u200b\u7684\u200b\u9644\u52a0\u200b\u6761\u6b3e\u200b\u90fd\u200b\u88ab\u200b\u89c6\u4e3a\u200b\u7b2c\u200b10\u200b\u6761\u200b\u610f\u4e49\u200b\u4e0a\u200b\u7684\u200b \u201c\u200b\u8fdb\u4e00\u6b65\u200b\u9650\u5236\u200b\u201d\u3002\u200b\u5982\u679c\u200b\u60a8\u200b\u6536\u5230\u200b\u7684\u200b\u7a0b\u5e8f\u200b\u6216\u200b\u5176\u200b\u4efb\u4f55\u200b\u90e8\u5206\u200b\u5305\u542b\u200b\u4e00\u4e2a\u200b\u901a\u77e5\u200b\uff0c\u200b\u8bf4\u660e\u200b\u5b83\u200b\u53d7\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u7ba1\u8f96\u200b\uff0c\u200b\u540c\u65f6\u200b\u8fd8\u6709\u200b\u4e00\u4e2a\u200b\u5c5e\u4e8e\u200b\u8fdb\u4e00\u6b65\u200b\u9650\u5236\u200b\u7684\u200b\u6761\u6b3e\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u5220\u9664\u200b\u8be5\u200b\u6761\u6b3e\u200b\u3002\u200b\u5982\u679c\u200b\u8bb8\u53ef\u200b\u6587\u4ef6\u200b\u5305\u542b\u200b\u8fdb\u4e00\u6b65\u200b\u7684\u200b\u9650\u5236\u200b\uff0c\u200b\u4f46\u200b\u5141\u8bb8\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u8fdb\u884c\u200b\u518d\u200b\u8bb8\u53ef\u200b\u6216\u200b\u8f6c\u8ba9\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u5728\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\u4e2d\u200b\u6dfb\u52a0\u200b\u53d7\u8be5\u200b\u8bb8\u53ef\u200b\u6587\u4ef6\u200b\u6761\u6b3e\u200b\u7ba1\u8f96\u200b\u7684\u200b\u6750\u6599\u200b\uff0c\u200b\u4f46\u200b\u8fdb\u4e00\u6b65\u200b\u7684\u200b\u9650\u5236\u200b\u5728\u200b\u8fd9\u79cd\u200b\u518d\u200b\u8bb8\u53ef\u200b\u6216\u200b\u8f6c\u8ba9\u200b\u4e2d\u200b\u4e0d\u200b\u5b58\u5728\u200b\u3002

\u200b\u5982\u679c\u200b\u60a8\u200b\u6309\u7167\u200b\u672c\u8282\u200b\u7684\u200b\u89c4\u5b9a\u200b\u5411\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\u6dfb\u52a0\u200b\u6761\u6b3e\u200b\uff0c\u200b\u60a8\u200b\u5fc5\u987b\u200b\u5728\u200b\u76f8\u5173\u200b\u7684\u200b\u6e90\u6587\u4ef6\u200b\u4e2d\u200b\u58f0\u660e\u200b\u9002\u7528\u200b\u4e8e\u200b\u8fd9\u4e9b\u200b\u6587\u4ef6\u200b\u7684\u200b\u9644\u52a0\u200b\u6761\u6b3e\u200b\uff0c\u200b\u6216\u8005\u200b\u8bf4\u660e\u200b\u5728\u200b\u54ea\u91cc\u200b\u53ef\u4ee5\u200b\u627e\u5230\u200b\u9002\u7528\u200b\u6761\u6b3e\u200b\u3002

\u200b\u989d\u5916\u200b\u7684\u200b\u6761\u6b3e\u200b\uff0c\u200b\u4e0d\u7ba1\u200b\u662f\u200b\u5141\u8bb8\u200b\u7684\u200b\u8fd8\u200b\u662f\u975e\u200b\u5141\u8bb8\u200b\u7684\u200b\uff0c\u200b\u90fd\u200b\u53ef\u4ee5\u200b\u4ee5\u200b\u5355\u72ec\u200b\u7684\u200b\u4e66\u9762\u200b\u8bb8\u53ef\u200b\u7684\u200b\u5f62\u5f0f\u200b\u8bf4\u660e\u200b\uff0c\u200b\u6216\u8005\u200b\u4f5c\u4e3a\u200b\u4f8b\u5916\u60c5\u51b5\u200b\u8bf4\u660e\u200b\uff1b\u200b\u4e0a\u8ff0\u200b\u8981\u6c42\u200b\u9002\u7528\u200b\u4e8e\u200b\u4efb\u4f55\u200b\u4e00\u79cd\u200b\u65b9\u5f0f\u200b\u3002

"},{"location":"zh/about/license/#8","title":"8. \u200b\u7ec8\u6b62\u200b.","text":"

\u200b\u9664\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u660e\u786e\u89c4\u5b9a\u200b\u7684\u200b\u60c5\u51b5\u200b\u5916\u200b\uff0c\u200b\u60a8\u200b\u4e0d\u5f97\u200b\u4f20\u64ad\u200b\u6216\u200b\u4fee\u6539\u200b\u8986\u76d6\u200b\u4f5c\u54c1\u200b\u3002\u200b\u4efb\u4f55\u200b\u4ee5\u200b\u5176\u4ed6\u200b\u65b9\u5f0f\u200b\u4f20\u64ad\u200b\u6216\u200b\u4fee\u6539\u200b\u4f5c\u54c1\u200b\u7684\u200b\u5c1d\u8bd5\u200b\u90fd\u200b\u662f\u200b\u65e0\u6548\u200b\u7684\u200b\uff0c\u200b\u5e76\u200b\u5c06\u200b\u81ea\u52a8\u200b\u7ec8\u6b62\u200b\u60a8\u200b\u5728\u200b\u672c\u200b\u8bb8\u53ef\u200b\u4e0b\u200b\u7684\u200b\u6743\u5229\u200b\uff08\u200b\u5305\u62ec\u200b\u6839\u636e\u200b\u7b2c\u200b11\u200b\u8282\u200b\u7b2c\u4e09\u6bb5\u200b\u6388\u4e88\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\uff09\u3002

\u200b\u7136\u800c\u200b\uff0c\u200b\u5982\u679c\u200b\u60a8\u200b\u505c\u6b62\u200b\u6240\u6709\u200b\u8fdd\u53cd\u200b\u672c\u200b\u8bb8\u53ef\u200b\u7684\u200b\u884c\u4e3a\u200b\uff0c\u200b\u90a3\u4e48\u200b\u60a8\u200b\u4ece\u200b\u67d0\u200b\u4e00\u200b\u7279\u5b9a\u200b\u7248\u6743\u200b\u4eba\u5904\u200b\u83b7\u5f97\u200b\u7684\u200b\u8bb8\u53ef\u200b\u5c06\u200b\u88ab\u200b\u6062\u590d\u200b\uff08a\uff09\u200b\u6682\u65f6\u6027\u200b\u7684\u200b\uff0c\u200b\u9664\u975e\u200b\u5e76\u200b\u76f4\u5230\u200b\u7248\u6743\u200b\u4eba\u200b\u660e\u786e\u200b\u5e76\u200b\u6700\u7ec8\u200b\u7ec8\u6b62\u200b\u60a8\u200b\u7684\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u4ee5\u53ca\u200b\uff08b\uff09\u200b\u6c38\u4e45\u6027\u200b\u7684\u200b\uff0c\u200b\u5982\u679c\u200b\u7248\u6743\u200b\u4eba\u200b\u672a\u80fd\u200b\u5728\u200b\u505c\u6b62\u200b\u540e\u200b\u7684\u200b60\u200b\u5929\u200b\u5185\u200b\u901a\u8fc7\u200b\u67d0\u79cd\u200b\u5408\u7406\u200b\u7684\u200b\u65b9\u5f0f\u200b\u901a\u77e5\u200b\u60a8\u200b\u4fb5\u6743\u884c\u4e3a\u200b\u3002

\u200b\u6b64\u5916\u200b\uff0c\u200b\u5982\u679c\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u4ee5\u200b\u67d0\u79cd\u200b\u5408\u7406\u200b\u7684\u200b\u65b9\u5f0f\u200b\u901a\u77e5\u200b\u60a8\u200b\u4fb5\u6743\u884c\u4e3a\u200b\uff0c\u200b\u8fd9\u200b\u662f\u200b\u60a8\u200b\u7b2c\u4e00\u6b21\u200b\u6536\u5230\u200b\u8be5\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u8fdd\u53cd\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u901a\u77e5\u200b\uff08\u200b\u9488\u5bf9\u200b\u4efb\u4f55\u200b\u4f5c\u54c1\u200b\uff09\uff0c\u200b\u5e76\u4e14\u200b\u60a8\u200b\u5728\u200b\u6536\u5230\u200b\u901a\u77e5\u200b\u540e\u200b30\u200b\u5929\u200b\u5185\u200b\u7ea0\u6b63\u200b\u4e86\u200b\u4fb5\u6743\u884c\u4e3a\u200b\uff0c\u200b\u90a3\u4e48\u200b\u60a8\u200b\u4ece\u200b\u67d0\u200b\u4e00\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u83b7\u5f97\u200b\u7684\u200b\u8bb8\u53ef\u8bc1\u200b\u5c06\u200b\u88ab\u200b\u6c38\u4e45\u200b\u6062\u590d\u200b\u3002

\u200b\u7ec8\u6b62\u200b\u60a8\u200b\u5728\u200b\u672c\u8282\u200b\u4e0b\u200b\u7684\u200b\u6743\u5229\u200b\u5e76\u200b\u4e0d\u200b\u7ec8\u6b62\u200b\u90a3\u4e9b\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4ece\u200b\u60a8\u200b\u90a3\u91cc\u200b\u83b7\u5f97\u200b\u526f\u672c\u200b\u6216\u200b\u6743\u5229\u200b\u7684\u200b\u5404\u65b9\u200b\u7684\u200b\u8bb8\u53ef\u200b\u3002\u200b\u5982\u679c\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b\u5df2\u7ecf\u200b\u88ab\u200b\u7ec8\u6b62\u200b\uff0c\u200b\u800c\u4e14\u200b\u6ca1\u6709\u200b\u6c38\u4e45\u200b\u6062\u590d\u200b\uff0c\u200b\u60a8\u200b\u5c31\u200b\u6ca1\u6709\u200b\u8d44\u683c\u200b\u6839\u636e\u200b\u7b2c\u200b10\u200b\u6761\u200b\u83b7\u5f97\u200b\u76f8\u540c\u200b\u6750\u6599\u200b\u7684\u200b\u65b0\u200b\u8bb8\u53ef\u200b\u3002

"},{"location":"zh/about/license/#9","title":"9. \u200b\u62e5\u6709\u200b\u526f\u672c\u200b\u4e0d\u200b\u9700\u8981\u200b\u63a5\u53d7\u200b.","text":"

\u200b\u60a8\u200b\u4e0d\u200b\u9700\u8981\u200b\u4e3a\u4e86\u200b\u63a5\u6536\u200b\u6216\u200b\u8fd0\u884c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u526f\u672c\u200b\u800c\u200b\u63a5\u53d7\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u3002\u200b\u4ec5\u4ec5\u200b\u7531\u4e8e\u200b\u4f7f\u7528\u200b\u70b9\u5bf9\u70b9\u200b\u4f20\u8f93\u200b\u6765\u200b\u63a5\u6536\u200b\u62f7\u8d1d\u200b\u800c\u200b\u53d1\u751f\u200b\u7684\u200b\u88ab\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u8f85\u52a9\u200b\u4f20\u64ad\u200b\uff0c\u200b\u4e5f\u200b\u540c\u6837\u200b\u4e0d\u200b\u9700\u8981\u200b\u63a5\u53d7\u200b\u3002\u200b\u7136\u800c\u200b\uff0c\u200b\u9664\u4e86\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e4b\u5916\u200b\uff0c\u200b\u6ca1\u6709\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u4e1c\u897f\u200b\u5141\u8bb8\u200b\u60a8\u200b\u4f20\u64ad\u200b\u6216\u200b\u4fee\u6539\u200b\u4efb\u4f55\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\u3002\u200b\u5982\u679c\u200b\u60a8\u200b\u4e0d\u200b\u63a5\u53d7\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u884c\u4e3a\u200b\u5c31\u200b\u4fb5\u72af\u200b\u4e86\u200b\u7248\u6743\u200b\u3002\u200b\u56e0\u6b64\u200b\uff0c\u200b\u901a\u8fc7\u200b\u4fee\u6539\u200b\u6216\u200b\u4f20\u64ad\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u60a8\u200b\u8868\u660e\u200b\u60a8\u200b\u63a5\u53d7\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u53ef\u4ee5\u200b\u8fd9\u6837\u200b\u505a\u200b\u3002

"},{"location":"zh/about/license/#10","title":"10. \u200b\u4e0b\u6e38\u200b\u63a5\u53d7\u8005\u200b\u7684\u200b\u81ea\u52a8\u200b\u8bb8\u53ef\u200b.","text":"

\u200b\u6bcf\u5f53\u200b\u60a8\u200b\u4f20\u9012\u200b\u4e00\u4e2a\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u65f6\u200b\uff0c\u200b\u63a5\u6536\u8005\u200b\u4f1a\u200b\u81ea\u52a8\u200b\u4ece\u200b\u539f\u59cb\u200b\u8bb8\u53ef\u200b\u4eba\u200b\u90a3\u91cc\u200b\u5f97\u5230\u200b\u4e00\u4e2a\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u5728\u200b\u9075\u5b88\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u524d\u63d0\u200b\u4e0b\u200b\uff0c\u200b\u8fd0\u884c\u200b\u3001\u200b\u4fee\u6539\u200b\u548c\u200b\u4f20\u64ad\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u3002\u200b\u60a8\u200b\u4e0d\u200b\u8d1f\u8d23\u200b\u6267\u884c\u200b\u7b2c\u4e09\u65b9\u200b\u5bf9\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u9075\u5b88\u200b\u3002

\u200b\u5b9e\u4f53\u200b\u4ea4\u6613\u200b \u201c\u200b\u662f\u200b\u6307\u200b\u8f6c\u8ba9\u200b\u4e00\u4e2a\u200b\u7ec4\u7ec7\u200b\u7684\u200b\u63a7\u5236\u6743\u200b\uff0c\u200b\u6216\u200b\u4e00\u4e2a\u200b\u7ec4\u7ec7\u200b\u7684\u200b\u5927\u90e8\u5206\u200b\u8d44\u4ea7\u200b\uff0c\u200b\u6216\u200b\u62c6\u5206\u200b\u4e00\u4e2a\u200b\u7ec4\u7ec7\u200b\uff0c\u200b\u6216\u200b\u5408\u5e76\u200b\u7ec4\u7ec7\u200b\u7684\u200b\u4ea4\u6613\u200b\u3002\u200b\u5982\u679c\u200b\u5b9e\u4f53\u200b\u4ea4\u6613\u200b\u5bfc\u81f4\u200b\u8986\u76d6\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4f20\u64ad\u200b\uff0c\u200b\u90a3\u4e48\u200b\u6536\u5230\u200b\u4f5c\u54c1\u200b\u526f\u672c\u200b\u7684\u200b\u6bcf\u200b\u4e00\u4e2a\u200b\u4ea4\u6613\u200b\u65b9\u200b\u4e5f\u200b\u4f1a\u200b\u6536\u5230\u200b\u8be5\u65b9\u200b\u7684\u200b\u6743\u76ca\u200b\u524d\u8eab\u200b\u6839\u636e\u200b\u524d\u6bb5\u200b\u89c4\u5b9a\u200b\u6240\u200b\u62e5\u6709\u200b\u6216\u200b\u53ef\u4ee5\u200b\u7ed9\u4e88\u200b\u7684\u200b\u4efb\u4f55\u200b\u4f5c\u54c1\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u4ece\u200b\u6743\u76ca\u200b\u524d\u200b\u8eab\u5904\u200b\u83b7\u5f97\u200b\u4f5c\u54c1\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u7684\u200b\u62e5\u6709\u6743\u200b\uff0c\u200b\u5982\u679c\u200b\u6743\u76ca\u200b\u524d\u8eab\u200b\u62e5\u6709\u200b\u6216\u200b\u901a\u8fc7\u200b\u5408\u7406\u200b\u52aa\u529b\u200b\u53ef\u4ee5\u200b\u83b7\u5f97\u200b\u3002

\u200b\u60a8\u200b\u4e0d\u5f97\u200b\u5bf9\u200b\u884c\u4f7f\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e0b\u200b\u6388\u4e88\u200b\u6216\u200b\u786e\u8ba4\u200b\u7684\u200b\u6743\u5229\u200b\u65bd\u52a0\u200b\u4efb\u4f55\u200b\u8fdb\u4e00\u6b65\u200b\u7684\u200b\u9650\u5236\u200b\u3002\u200b\u4f8b\u5982\u200b\uff0c\u200b\u60a8\u200b\u4e0d\u5f97\u200b\u5bf9\u200b\u884c\u4f7f\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6240\u200b\u6388\u4e88\u200b\u7684\u200b\u6743\u5229\u200b\u5f81\u6536\u200b\u8bb8\u53ef\u8d39\u200b\u3001\u200b\u7279\u8bb8\u6743\u200b\u4f7f\u7528\u8d39\u200b\u6216\u200b\u5176\u4ed6\u8d39\u7528\u200b\uff0c\u200b\u4e5f\u200b\u4e0d\u5f97\u200b\u63d0\u8d77\u200b\u8bc9\u8bbc\u200b\uff08\u200b\u5305\u62ec\u200b\u8bc9\u8bbc\u200b\u4e2d\u200b\u7684\u200b\u4ea4\u53c9\u200b\u7d22\u8d54\u200b\u6216\u200b\u53cd\u200b\u7d22\u8d54\u200b\uff09\uff0c\u200b\u6307\u63a7\u200b\u5236\u4f5c\u200b\u3001\u200b\u4f7f\u7528\u200b\u3001\u200b\u9500\u552e\u200b\u3001\u200b\u63d0\u4f9b\u200b\u9500\u552e\u200b\u6216\u200b\u8fdb\u53e3\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u6216\u200b\u5176\u200b\u4efb\u4f55\u200b\u90e8\u5206\u200b\u4fb5\u72af\u200b\u4e86\u200b\u4efb\u4f55\u200b\u4e13\u5229\u200b\u6743\u5229\u200b\u3002

"},{"location":"zh/about/license/#11","title":"11. \u200b\u4e13\u5229\u200b.","text":"

\u200b\u8d21\u732e\u8005\u200b \u201c\u200b\u662f\u200b\u6307\u200b\u6388\u6743\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4f7f\u7528\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u6216\u672c\u200b\u7a0b\u5e8f\u200b\u6240\u200b\u57fa\u4e8e\u200b\u7684\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u3002\u200b\u8fd9\u6837\u200b\u6388\u6743\u200b\u7684\u200b\u4f5c\u54c1\u200b\u88ab\u200b\u79f0\u4e3a\u200b\u8d21\u732e\u8005\u200b\u7684\u200b \u201c\u200b\u8d21\u732e\u8005\u200b\u7248\u672c\u200b\u201d\u3002

\u200b\u8d21\u732e\u8005\u200b\u7684\u200b \u201c\u200b\u57fa\u672c\u200b\u4e13\u5229\u200b\u6743\u5229\u200b\u8981\u6c42\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u8d21\u732e\u8005\u200b\u62e5\u6709\u200b\u6216\u200b\u63a7\u5236\u200b\u7684\u200b\u6240\u6709\u200b\u4e13\u5229\u200b\u6743\u5229\u200b\u8981\u6c42\u200b\uff0c\u200b\u4e0d\u7ba1\u200b\u662f\u200b\u5df2\u7ecf\u200b\u83b7\u5f97\u200b\u7684\u200b\u8fd8\u662f\u200b\u4ee5\u540e\u200b\u83b7\u5f97\u200b\u7684\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u4e13\u5229\u200b\u6743\u5229\u200b\u8981\u6c42\u200b\u5c06\u200b\u88ab\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u5141\u8bb8\u200b\u7684\u200b\u67d0\u79cd\u200b\u65b9\u5f0f\u200b\u6240\u200b\u4fb5\u72af\u200b\uff0c\u200b\u5373\u200b\u5236\u4f5c\u200b\u3001\u200b\u4f7f\u7528\u200b\u6216\u200b\u9500\u552e\u200b\u5176\u200b\u8d21\u732e\u8005\u200b\u7248\u672c\u200b\uff0c\u200b\u4f46\u200b\u4e0d\u200b\u5305\u62ec\u200b\u4ec5\u200b\u56e0\u200b\u8fdb\u4e00\u6b65\u200b\u4fee\u6539\u200b\u8d21\u732e\u8005\u200b\u7248\u672c\u200b\u800c\u200b\u88ab\u200b\u4fb5\u72af\u200b\u7684\u200b\u6743\u5229\u200b\u8981\u6c42\u200b\u3002\u200b\u5c31\u200b\u672c\u200b\u5b9a\u4e49\u200b\u800c\u8a00\u200b\uff0c\u201d\u200b\u63a7\u5236\u200b\u201d \u200b\u5305\u62ec\u200b\u4ee5\u200b\u7b26\u5408\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u8981\u6c42\u200b\u7684\u200b\u65b9\u5f0f\u200b\u6388\u4e88\u200b\u4e13\u5229\u200b\u5206\u200b\u8bb8\u53ef\u200b\u7684\u200b\u6743\u5229\u200b\u3002

\u200b\u6bcf\u4e2a\u200b\u8d21\u732e\u8005\u200b\u6839\u636e\u200b\u8d21\u732e\u8005\u200b\u7684\u200b\u57fa\u672c\u200b\u4e13\u5229\u200b\u6743\u5229\u200b\u8981\u6c42\u200b\uff0c\u200b\u6388\u4e88\u200b\u60a8\u200b\u975e\u200b\u72ec\u5360\u6027\u200b\u7684\u200b\u3001\u200b\u5168\u7403\u6027\u200b\u7684\u200b\u3001\u200b\u514d\u200b\u7248\u7a0e\u200b\u7684\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u4ee5\u200b\u5236\u9020\u200b\u3001\u200b\u4f7f\u7528\u200b\u3001\u200b\u9500\u552e\u200b\u3001\u200b\u63d0\u4f9b\u200b\u9500\u552e\u200b\u3001\u200b\u8fdb\u53e3\u200b\u548c\u200b\u4ee5\u200b\u5176\u4ed6\u200b\u65b9\u5f0f\u200b\u8fd0\u884c\u200b\u3001\u200b\u4fee\u6539\u200b\u548c\u200b\u4f20\u64ad\u200b\u5176\u200b\u8d21\u732e\u8005\u200b\u7248\u672c\u200b\u7684\u200b\u5185\u5bb9\u200b\u3002

\u200b\u5728\u200b\u4ee5\u4e0b\u200b\u4e09\u6bb5\u200b\u4e2d\u200b\uff0c\u201d\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u4e0d\u200b\u5b9e\u65bd\u200b\u4e13\u5229\u200b\u7684\u200b\u4efb\u4f55\u200b\u660e\u793a\u200b\u534f\u8bae\u200b\u6216\u200b\u627f\u8bfa\u200b\uff0c\u200b\u65e0\u8bba\u200b\u5176\u200b\u540d\u79f0\u200b\u5982\u4f55\u200b\uff08\u200b\u4f8b\u5982\u200b\uff0c\u200b\u660e\u786e\u200b\u5141\u8bb8\u200b\u5b9e\u65bd\u200b\u4e13\u5229\u200b\u6216\u200b\u4e0d\u200b\u8d77\u8bc9\u200b\u4e13\u5229\u200b\u4fb5\u6743\u200b\u7684\u200b\u7ea6\u5b9a\u200b\uff09\u3002\u200b\u5411\u200b\u4e00\u65b9\u200b \u201c\u200b\u6388\u4e88\u200b\u201d \u200b\u8fd9\u79cd\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u610f\u5473\u7740\u200b\u4f5c\u51fa\u200b\u8fd9\u79cd\u200b\u534f\u8bae\u200b\u6216\u200b\u627f\u8bfa\u200b\uff0c\u200b\u4e0d\u200b\u5bf9\u200b\u8be5\u65b9\u200b\u5b9e\u65bd\u200b\u4e13\u5229\u200b\u3002

\u200b\u5982\u679c\u200b\u60a8\u200b\u5728\u200b\u77e5\u60c5\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u4f9d\u9760\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u4f20\u9012\u200b\u4e86\u200b\u4e00\u4e2a\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u800c\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u7684\u200b\u76f8\u5e94\u200b\u6765\u6e90\u200b\u5e76\u200b\u6ca1\u6709\u200b\u901a\u8fc7\u200b\u516c\u5f00\u200b\u7684\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u200b\u6216\u200b\u5176\u4ed6\u200b\u5bb9\u6613\u200b\u83b7\u5f97\u200b\u7684\u200b\u65b9\u5f0f\u200b\uff0c\u200b\u4f9b\u200b\u4efb\u4f55\u4eba\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u6761\u6b3e\u200b\u514d\u8d39\u200b\u590d\u5236\u200b\uff0c\u200b\u90a3\u4e48\u200b\u60a8\u200b\u5fc5\u987b\u200b(1)\u200b\u4f7f\u200b\u76f8\u5e94\u200b\u6765\u6e90\u200b\u53ef\u4ee5\u200b\u83b7\u5f97\u200b\uff0c\u200b\u6216\u8005\u200b(2)\u200b\u5b89\u6392\u200b\u5265\u593a\u200b\u81ea\u5df1\u200b\u5bf9\u200b\u8be5\u200b\u7279\u5b9a\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u5229\u76ca\u200b\uff0c\u200b\u6216\u8005\u200b(3)\u200b\u4ee5\u200b\u7b26\u5408\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u8981\u6c42\u200b\u7684\u200b\u65b9\u5f0f\u200b\uff0c\u200b\u5b89\u6392\u200b\u5c06\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u6269\u5c55\u200b\u5230\u200b\u4e0b\u6e38\u200b\u63a5\u53d7\u8005\u200b\u3002\u201d\u200b\u660e\u77e5\u6545\u72af\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u60a8\u200b\u5b9e\u9645\u200b\u77e5\u9053\u200b\uff0c\u200b\u5982\u679c\u200b\u6ca1\u6709\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u60a8\u200b\u5728\u200b\u67d0\u4e2a\u200b\u56fd\u5bb6\u200b\u4f20\u9012\u200b\u6240\u6d89\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u6216\u8005\u200b\u60a8\u200b\u7684\u200b\u63a5\u53d7\u8005\u200b\u5728\u200b\u67d0\u4e2a\u200b\u56fd\u5bb6\u200b\u4f7f\u7528\u200b\u6240\u6d89\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u5c06\u200b\u4fb5\u72af\u200b\u60a8\u200b\u6709\u200b\u7406\u7531\u200b\u76f8\u4fe1\u200b\u5728\u200b\u8be5\u56fd\u200b\u6709\u6548\u200b\u7684\u200b\u4e00\u9879\u200b\u6216\u200b\u591a\u9879\u200b\u53ef\u200b\u8bc6\u522b\u200b\u4e13\u5229\u200b\u3002

\u200b\u5982\u679c\u200b\u6839\u636e\u200b\u4e00\u9879\u200b\u4ea4\u6613\u200b\u6216\u200b\u5b89\u6392\u200b\u6216\u200b\u4e0e\u200b\u4e4b\u200b\u76f8\u5173\u200b\uff0c\u200b\u60a8\u200b\u8f6c\u8ba9\u200b\u6216\u200b\u901a\u8fc7\u200b\u4fc3\u6210\u200b\u8f6c\u8ba9\u200b\u4f20\u64ad\u200b\u88ab\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u5e76\u200b\u5411\u200b\u63a5\u53d7\u200b\u88ab\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u67d0\u4e9b\u200b\u5f53\u4e8b\u65b9\u200b\u6388\u4e88\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u6388\u6743\u200b\u4ed6\u4eec\u200b\u4f7f\u7528\u200b\u3001\u200b\u4f20\u64ad\u200b\u3001\u200b\u4fee\u6539\u200b\u6216\u200b\u8f6c\u8ba9\u200b\u88ab\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7279\u5b9a\u200b\u526f\u672c\u200b\uff0c\u200b\u90a3\u4e48\u200b\u60a8\u200b\u6388\u4e88\u200b\u7684\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u5c06\u200b\u81ea\u52a8\u200b\u6269\u5c55\u200b\u5230\u200b\u88ab\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u548c\u200b\u57fa\u4e8e\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u7684\u200b\u6240\u6709\u200b\u63a5\u53d7\u8005\u200b\u3002

\u200b\u5982\u679c\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u4e0d\u200b\u5305\u62ec\u200b\u5728\u200b\u5176\u200b\u8986\u76d6\u8303\u56f4\u200b\u5185\u200b\uff0c\u200b\u7981\u6b62\u200b\u884c\u4f7f\u200b\u6216\u200b\u4ee5\u200b\u4e0d\u200b\u884c\u4f7f\u200b\u672c\u200b\u8bb8\u53ef\u200b\u5177\u4f53\u200b\u6388\u4e88\u200b\u7684\u200b\u4e00\u9879\u200b\u6216\u200b\u591a\u9879\u200b\u6743\u5229\u200b\u4e3a\u200b\u6761\u4ef6\u200b\uff0c\u200b\u5219\u200b\u4e3a\u200b \u201c\u200b\u6b67\u89c6\u6027\u200b\u7684\u200b\u201d\u3002\u200b\u5982\u679c\u200b\u60a8\u200b\u662f\u200b\u4e0e\u200b\u4ece\u4e8b\u200b\u8f6f\u4ef6\u200b\u5206\u9500\u200b\u4e1a\u52a1\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u8fbe\u6210\u200b\u7684\u200b\u5b89\u6392\u200b\u7684\u200b\u4e00\u65b9\u200b\uff0c\u200b\u6839\u636e\u200b\u8be5\u200b\u5b89\u6392\u200b\uff0c\u200b\u60a8\u200b\u6839\u636e\u200b\u60a8\u200b\u4f20\u9012\u200b\u4f5c\u54c1\u200b\u7684\u200b\u6d3b\u52a8\u200b\u8303\u56f4\u200b\u5411\u200b\u7b2c\u4e09\u65b9\u200b\u4ed8\u6b3e\u200b\uff0c\u200b\u5e76\u4e14\u200b\u6839\u636e\u200b\u8be5\u200b\u5b89\u6392\u200b\uff0c\u200b\u7b2c\u4e09\u200b\u65b9\u5411\u200b\u4efb\u4f55\u200b\u5c06\u200b\u4ece\u200b\u60a8\u200b\u90a3\u91cc\u200b\u83b7\u5f97\u200b\u6240\u6d89\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4e00\u65b9\u200b\u6388\u4e88\u200b\uff0c\u200b\u5219\u200b\u60a8\u200b\u4e0d\u5f97\u200b\u4f20\u9012\u200b\u6240\u6d89\u200b\u4f5c\u54c1\u200b\u3002\u200b\u6b67\u89c6\u6027\u200b\u7684\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\uff1a(a)\u200b\u4e0e\u200b\u60a8\u200b\u4f20\u9012\u200b\u7684\u200b\u4f5c\u54c1\u200b\u7684\u200b\u526f\u672c\u200b\uff08\u200b\u6216\u200b\u7531\u200b\u8fd9\u4e9b\u200b\u526f\u672c\u200b\u5236\u4f5c\u200b\u7684\u200b\u526f\u672c\u200b\uff09\u200b\u6709\u5173\u200b\uff0c\u200b\u6216\u200b(b)\u200b\u4e3b\u8981\u200b\u4e3a\u200b\u5305\u542b\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7279\u5b9a\u200b\u4ea7\u54c1\u200b\u6216\u200b\u6c47\u7f16\u200b\u5e76\u200b\u4e0e\u200b\u4e4b\u200b\u6709\u5173\u200b\uff0c\u200b\u9664\u975e\u200b\u60a8\u200b\u5728\u200b2007\u200b\u5e74\u200b3\u200b\u6708\u200b28\u200b\u65e5\u200b\u4e4b\u524d\u200b\u8fbe\u6210\u200b\u8be5\u200b\u5b89\u6392\u200b\uff0c\u200b\u6216\u200b\u6388\u4e88\u200b\u8be5\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u3002

\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e2d\u200b\u7684\u200b\u4efb\u4f55\u200b\u5185\u5bb9\u200b\u90fd\u200b\u4e0d\u5e94\u200b\u88ab\u200b\u89e3\u91ca\u200b\u4e3a\u200b\u6392\u9664\u200b\u6216\u200b\u9650\u5236\u200b\u4efb\u4f55\u200b\u9690\u542b\u200b\u7684\u200b\u8bb8\u53ef\u200b\u6216\u200b\u5176\u4ed6\u200b\u5bf9\u200b\u4fb5\u6743\u200b\u7684\u200b\u6297\u8fa9\u200b\uff0c\u200b\u6839\u636e\u200b\u9002\u7528\u200b\u7684\u200b\u4e13\u5229\u6cd5\u200b\uff0c\u200b\u60a8\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u6709\u200b\u8fd9\u6837\u200b\u7684\u200b\u673a\u4f1a\u200b\u3002

"},{"location":"zh/about/license/#12","title":"12. \u200b\u4e0d\u200b\u653e\u5f03\u200b\u4ed6\u4eba\u200b\u7684\u200b\u81ea\u7531\u200b.","text":"

\u200b\u5982\u679c\u200b\u5f3a\u52a0\u200b\u7ed9\u200b\u60a8\u200b\u7684\u200b\u6761\u4ef6\u200b\uff08\u200b\u65e0\u8bba\u662f\u200b\u901a\u8fc7\u200b\u6cd5\u9662\u200b\u547d\u4ee4\u200b\u3001\u200b\u534f\u8bae\u200b\u6216\u200b\u5176\u4ed6\u200b\u65b9\u5f0f\u200b\uff09\u200b\u4e0e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u6761\u4ef6\u200b\u76f8\u200b\u62b5\u89e6\u200b\uff0c\u200b\u5b83\u4eec\u200b\u5e76\u200b\u4e0d\u80fd\u200b\u514d\u9664\u200b\u60a8\u200b\u5bf9\u200b\u672c\u200b\u8bb8\u53ef\u200b\u6761\u4ef6\u200b\u7684\u200b\u9075\u5b88\u200b\u3002\u200b\u5982\u679c\u200b\u60a8\u200b\u4e0d\u80fd\u200b\u5728\u200b\u8f6c\u8ba9\u200b\u4f5c\u54c1\u200b\u65f6\u200b\u540c\u65f6\u200b\u6ee1\u8db3\u200b\u60a8\u200b\u5728\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e0b\u200b\u7684\u200b\u4e49\u52a1\u200b\u548c\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u76f8\u5173\u200b\u7684\u200b\u4e49\u52a1\u200b\uff0c\u200b\u90a3\u4e48\u200b\u4f5c\u4e3a\u200b\u7ed3\u679c\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u4e0d\u200b\u8f6c\u8ba9\u200b\u5b83\u200b\u3002\u200b\u4f8b\u5982\u200b\uff0c\u200b\u5982\u679c\u200b\u60a8\u200b\u540c\u610f\u200b\u7684\u200b\u6761\u6b3e\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u60a8\u200b\u6709\u200b\u4e49\u52a1\u200b\u5411\u200b\u63a5\u53d7\u200b\u60a8\u200b\u4f20\u9001\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u4eba\u200b\u6536\u53d6\u200b\u7248\u7a0e\u200b\uff0c\u200b\u90a3\u4e48\u200b\u60a8\u200b\u8981\u200b\u540c\u65f6\u200b\u6ee1\u8db3\u200b\u8fd9\u4e9b\u200b\u6761\u6b3e\u200b\u548c\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u8981\u6c42\u200b\uff0c\u200b\u552f\u4e00\u200b\u7684\u200b\u529e\u6cd5\u200b\u5c31\u662f\u200b\u5b8c\u5168\u200b\u4e0d\u200b\u4f20\u9001\u200b\u8be5\u200b\u7a0b\u5e8f\u200b\u3002

"},{"location":"zh/about/license/#13-gnu","title":"13. \u200b\u8fdc\u7a0b\u200b\u7f51\u7edc\u200b\u4ea4\u4e92\u200b\uff1b\u200b\u4e0e\u200bGNU\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u4e00\u8d77\u200b\u4f7f\u7528\u200b.","text":"

\u200b\u5c3d\u7ba1\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6709\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u5982\u679c\u200b\u60a8\u200b\u4fee\u6539\u200b\u672c\u200b\u7a0b\u5e8f\u200b\uff0c\u200b\u60a8\u200b\u7684\u200b\u4fee\u6539\u200b\u7248\u672c\u200b\u5fc5\u987b\u200b\u5728\u200b\u663e\u8457\u200b\u4f4d\u7f6e\u200b\u5411\u200b\u6240\u6709\u200b\u901a\u8fc7\u200b\u8ba1\u7b97\u673a\u7f51\u7edc\u200b\u8fdc\u7a0b\u200b\u4e0e\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u4e92\u52a8\u200b\u7684\u200b\u7528\u6237\u200b\uff08\u200b\u5982\u679c\u200b\u60a8\u200b\u7684\u200b\u7248\u672c\u200b\u652f\u6301\u200b\u8fd9\u79cd\u200b\u4e92\u52a8\u200b\uff09\u200b\u63d0\u4f9b\u200b\u673a\u4f1a\u200b\uff0c\u200b\u901a\u8fc7\u200b\u4e00\u4e9b\u200b\u6807\u51c6\u200b\u6216\u200b\u4e60\u60ef\u200b\u7684\u200b\u4fc3\u8fdb\u200b\u8f6f\u4ef6\u200b\u590d\u5236\u200b\u7684\u200b\u65b9\u5f0f\u200b\uff0c\u200b\u4ece\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\u514d\u8d39\u200b\u63d0\u4f9b\u200b\u76f8\u5e94\u200b\u7684\u200b\u6e90\u7801\u200b\u3002\u200b\u8be5\u200b\u76f8\u5e94\u200b\u6e90\u7801\u200b\u5e94\u200b\u5305\u62ec\u200b\u6839\u636e\u200b\u4e0b\u200b\u6bb5\u200b\u89c4\u5b9a\u200b\u7eb3\u5165\u200bGNU\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7b2c\u200b3\u200b\u7248\u200b\u7684\u200b\u4efb\u4f55\u200b\u4f5c\u54c1\u200b\u7684\u200b\u76f8\u5e94\u200b\u6e90\u7801\u200b\u3002

\u200b\u5c3d\u7ba1\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6709\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u60a8\u200b\u6709\u200b\u6743\u5229\u200b\u5c06\u200b\u4efb\u4f55\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\u4e0e\u200b\u5728\u200bGNU\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7b2c\u200b3\u200b\u7248\u4e0b\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u4f5c\u54c1\u200b\u94fe\u63a5\u200b\u6216\u200b\u7ed3\u5408\u200b\u6210\u200b\u4e00\u4e2a\u200b\u5355\u4e00\u200b\u7684\u200b\u7ec4\u5408\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u5e76\u200b\u4f20\u9012\u200b\u7531\u6b64\u200b\u4ea7\u751f\u200b\u7684\u200b\u4f5c\u54c1\u200b\u3002\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u6761\u6b3e\u200b\u5c06\u200b\u7ee7\u7eed\u200b\u9002\u7528\u200b\u4e8e\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u90e8\u5206\u200b\uff0c\u200b\u4f46\u200b\u4e0e\u200b\u4e4b\u200b\u7ed3\u5408\u200b\u7684\u200b\u4f5c\u54c1\u200b\u5c06\u200b\u7ee7\u7eed\u200b\u53d7\u200bGNU\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7b2c\u200b3\u200b\u7248\u200b\u7684\u200b\u7ba1\u8f96\u200b\u3002

"},{"location":"zh/about/license/#14","title":"14. \u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u4fee\u8ba2\u7248\u200b.","text":"

\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u57fa\u91d1\u4f1a\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u4e0d\u65f6\u200b\u5730\u200b\u53d1\u5e03\u200bGNU Affero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u4fee\u8ba2\u7248\u200b\u548c\u200b/\u200b\u6216\u200b\u65b0\u200b\u7248\u672c\u200b\u3002\u200b\u8fd9\u4e9b\u200b\u65b0\u200b\u7248\u672c\u200b\u5728\u7cbe\u795e\u4e0a\u200b\u4e0e\u200b\u76ee\u524d\u200b\u7684\u200b\u7248\u672c\u200b\u76f8\u4f3c\u200b\uff0c\u200b\u4f46\u200b\u5728\u200b\u7ec6\u8282\u200b\u4e0a\u200b\u53ef\u80fd\u200b\u6709\u6240\u4e0d\u540c\u200b\uff0c\u200b\u4ee5\u200b\u89e3\u51b3\u200b\u65b0\u200b\u7684\u200b\u95ee\u9898\u200b\u6216\u200b\u5173\u5207\u200b\u3002

\u200b\u6bcf\u4e2a\u200b\u7248\u672c\u200b\u90fd\u200b\u6709\u200b\u4e00\u4e2a\u200b\u533a\u5206\u200b\u7684\u200b\u7248\u672c\u53f7\u200b\u3002\u200b\u5982\u679c\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u6307\u5b9a\u200b\u67d0\u4e2a\u200b\u7f16\u53f7\u200b\u7684\u200b GNU Affero \u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b \u201c\u200b\u6216\u200b\u4efb\u4f55\u200b\u540e\u6765\u200b\u7684\u200b\u7248\u672c\u200b\u201d \u200b\u9002\u7528\u200b\u4e8e\u200b\u5b83\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u9009\u62e9\u200b\u9075\u5b88\u200b\u8be5\u200b\u7f16\u53f7\u200b\u7684\u200b\u7248\u672c\u200b\u6216\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u57fa\u91d1\u4f1a\u200b\u53d1\u5e03\u200b\u7684\u200b\u4efb\u4f55\u200b\u540e\u6765\u200b\u7684\u200b\u7248\u672c\u200b\u7684\u200b\u6761\u6b3e\u200b\u548c\u200b\u6761\u4ef6\u200b\u3002\u200b\u5982\u679c\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u6ca1\u6709\u200b\u6307\u5b9a\u200b GNU Affero \u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u7248\u672c\u53f7\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u9009\u62e9\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u57fa\u91d1\u4f1a\u200b\u53d1\u5e03\u200b\u7684\u200b\u4efb\u4f55\u200b\u7248\u672c\u200b\u3002 \u200b\u5982\u679c\u200b\u672c\u200b\u8ba1\u5212\u200b\u89c4\u5b9a\u200b\u4ee3\u7406\u4eba\u200b\u53ef\u4ee5\u200b\u51b3\u5b9a\u200b\u672a\u6765\u200b\u53ef\u4ee5\u200b\u4f7f\u7528\u200b\u54ea\u4e2a\u200b\u7248\u672c\u200b\u7684\u200b GNU Affero \u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u90a3\u4e48\u200b\u8be5\u200b\u4ee3\u7406\u4eba\u200b\u5bf9\u200b\u67d0\u4e2a\u200b\u7248\u672c\u200b\u7684\u200b\u516c\u5f00\u200b\u63a5\u53d7\u200b\u58f0\u660e\u200b\u5c06\u200b\u6c38\u4e45\u200b\u6388\u6743\u200b\u60a8\u200b\u4e3a\u200b\u672c\u200b\u8ba1\u5212\u200b\u9009\u62e9\u200b\u8be5\u200b\u7248\u672c\u200b\u3002

\u200b\u4ee5\u540e\u200b\u7684\u200b\u8bb8\u53ef\u8bc1\u200b\u7248\u672c\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u7ed9\u200b\u60a8\u200b\u989d\u5916\u200b\u7684\u200b\u6216\u200b\u4e0d\u540c\u200b\u7684\u200b\u6743\u9650\u200b\u3002\u200b\u4f46\u662f\u200b\uff0c\u200b\u4efb\u4f55\u200b\u4f5c\u8005\u200b\u6216\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u90fd\u200b\u4e0d\u4f1a\u200b\u56e0\u4e3a\u200b\u60a8\u200b\u9009\u62e9\u200b\u4e86\u200b\u540e\u6765\u200b\u7684\u200b\u7248\u672c\u200b\u800c\u200b\u627f\u62c5\u200b\u989d\u5916\u200b\u7684\u200b\u4e49\u52a1\u200b\u3002

"},{"location":"zh/about/license/#15","title":"15. \u200b\u514d\u8d23\u200b\u58f0\u660e\u200b.","text":"

\u200b\u5728\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u5141\u8bb8\u200b\u7684\u200b\u8303\u56f4\u200b\u5185\u200b\uff0c\u200b\u5bf9\u200b\u8be5\u200b\u7a0b\u5e8f\u200b\u6ca1\u6709\u200b\u4efb\u4f55\u200b\u4fdd\u8bc1\u200b\u3002\u200b\u9664\u975e\u200b\u53e6\u6709\u200b\u4e66\u9762\u200b\u8bf4\u660e\u200b\uff0c\u200b\u5426\u5219\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u548c\u200b/\u200b\u6216\u200b\u5176\u4ed6\u200b\u5404\u65b9\u200b \u201c\u200b\u6309\u200b\u539f\u6837\u200b\u201d \u200b\u63d0\u4f9b\u200b\u8be5\u200b\u7a0b\u5e8f\u200b\uff0c\u200b\u4e0d\u200b\u63d0\u4f9b\u200b\u4efb\u4f55\u200b\u660e\u793a\u200b\u6216\u200b\u6697\u793a\u200b\u7684\u200b\u4fdd\u8bc1\u200b\uff0c\u200b\u5305\u62ec\u200b\u4f46\u200b\u4e0d\u200b\u9650\u4e8e\u200b\u5bf9\u200b\u9002\u9500\u200b\u6027\u200b\u548c\u200b\u7279\u5b9a\u200b\u7528\u9014\u200b\u7684\u200b\u9002\u7528\u6027\u200b\u7684\u200b\u6697\u793a\u200b\u4fdd\u8bc1\u200b\u3002\u200b\u5173\u4e8e\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u8d28\u91cf\u200b\u548c\u200b\u6027\u80fd\u200b\u7684\u200b\u5168\u90e8\u200b\u98ce\u9669\u200b\u7531\u200b\u60a8\u200b\u627f\u62c5\u200b\u3002\u200b\u5982\u679c\u200b\u8be5\u200b\u7a0b\u5e8f\u200b\u88ab\u200b\u8bc1\u660e\u200b\u6709\u200b\u7f3a\u9677\u200b\uff0c\u200b\u60a8\u200b\u5c06\u200b\u627f\u62c5\u200b\u6240\u6709\u200b\u5fc5\u8981\u200b\u7684\u200b\u670d\u52a1\u200b\u3001\u200b\u4fee\u7406\u200b\u6216\u200b\u7ea0\u6b63\u200b\u7684\u200b\u8d39\u7528\u200b\u3002

"},{"location":"zh/about/license/#16","title":"16. \u200b\u8d54\u507f\u200b\u8d23\u4efb\u200b\u7684\u200b\u9650\u5236\u200b.","text":"

\u200b\u5728\u200b\u4efb\u4f55\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u9664\u975e\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u8981\u6c42\u200b\u6216\u200b\u4e66\u9762\u200b\u540c\u610f\u200b\uff0c\u200b\u4efb\u4f55\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u6216\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u6309\u200b\u4e0a\u8ff0\u200b\u89c4\u5b9a\u200b\u4fee\u6539\u200b\u548c\u200b/\u200b\u6216\u200b\u4f20\u9012\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u5f53\u4e8b\u4eba\u200b\u90fd\u200b\u4e0d\u200b\u5bf9\u200b\u60a8\u200b\u7684\u200b\u635f\u5bb3\u200b\u8d1f\u8d23\u200b\uff0c\u200b\u5305\u62ec\u200b\u56e0\u200b\u4f7f\u7528\u200b\u6216\u200b\u65e0\u6cd5\u200b\u4f7f\u7528\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u800c\u200b\u5f15\u8d77\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e00\u822c\u200b\u7684\u200b\u3001\u200b\u7279\u6b8a\u200b\u7684\u200b\u3001\u200b\u5076\u7136\u200b\u7684\u200b\u6216\u200b\u95f4\u63a5\u200b\u7684\u200b\u635f\u5bb3\u200b\uff08\u200b\u5305\u62ec\u200b\u4f46\u200b\u4e0d\u200b\u9650\u4e8e\u200b\u6570\u636e\u200b\u4e22\u5931\u200b\u6216\u200b\u6570\u636e\u200b\u4e0d\u200b\u51c6\u786e\u200b\u6216\u200b\u60a8\u200b\u6216\u200b\u7b2c\u4e09\u65b9\u200b\u906d\u53d7\u200b\u7684\u200b\u635f\u5931\u200b\u6216\u672c\u200b\u7a0b\u5e8f\u200b\u65e0\u6cd5\u200b\u4e0e\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u7a0b\u5e8f\u200b\u4e00\u8d77\u200b\u8fd0\u884c\u200b\uff09\uff0c\u200b\u5373\u4f7f\u200b\u8be5\u200b\u6301\u6709\u4eba\u200b\u6216\u200b\u5176\u4ed6\u200b\u5f53\u4e8b\u4eba\u200b\u5df2\u200b\u88ab\u200b\u544a\u77e5\u200b\u8fd9\u79cd\u200b\u635f\u5bb3\u200b\u7684\u200b\u53ef\u80fd\u6027\u200b\u3002

"},{"location":"zh/about/license/#17-1516","title":"17. \u200b\u7b2c\u200b15\u200b\u6761\u200b\u548c\u200b\u7b2c\u200b16\u200b\u6761\u200b\u7684\u200b\u89e3\u91ca\u200b.","text":"

\u200b\u5982\u679c\u200b\u4ee5\u4e0a\u200b\u89c4\u5b9a\u200b\u7684\u200b\u514d\u8d23\u200b\u58f0\u660e\u200b\u548c\u200b\u8d23\u4efb\u200b\u9650\u5236\u200b\u4e0d\u80fd\u200b\u6839\u636e\u200b\u5176\u200b\u6761\u6b3e\u200b\u5728\u200b\u5f53\u5730\u200b\u4ea7\u751f\u200b\u6cd5\u5f8b\u6548\u529b\u200b\uff0c\u200b\u5ba1\u67e5\u200b\u6cd5\u9662\u200b\u5e94\u200b\u9002\u7528\u200b\u6700\u200b\u63a5\u8fd1\u200b\u4e8e\u200b\u7edd\u5bf9\u200b\u653e\u5f03\u200b\u4e0e\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u6709\u5173\u200b\u7684\u200b\u6240\u6709\u200b\u6c11\u4e8b\u8d23\u4efb\u200b\u7684\u200b\u5f53\u5730\u200b\u6cd5\u5f8b\u200b\uff0c\u200b\u9664\u975e\u200b\u5728\u200b\u6536\u53d6\u200b\u8d39\u7528\u200b\u7684\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u526f\u672c\u200b\u4e0a\u200b\u9644\u6709\u200b\u4fdd\u8bc1\u200b\u6216\u200b\u8d23\u4efb\u200b\u627f\u62c5\u200b\u3002

\u200b\u4ee5\u4e0a\u200b\u662f\u200b\u6761\u6b3e\u200b\u548c\u200b\u6761\u4ef6\u200b

"},{"location":"zh/about/license/#_3","title":"\u5982\u4f55\u200b\u5c06\u200b\u8fd9\u4e9b\u200b\u6761\u6b3e\u200b\u5e94\u7528\u200b\u4e8e\u200b\u60a8\u200b\u7684\u200b\u65b0\u200b\u7a0b\u5e8f","text":"

\u200b\u5982\u679c\u200b\u60a8\u200b\u5f00\u53d1\u200b\u4e86\u200b\u4e00\u4e2a\u200b\u65b0\u200b\u7684\u200b\u7a0b\u5e8f\u200b\uff0c\u200b\u5e76\u200b\u5e0c\u671b\u200b\u5b83\u200b\u5bf9\u200b\u516c\u4f17\u200b\u6709\u200b\u6700\u5927\u200b\u7684\u200b\u7528\u5904\u200b\uff0c\u200b\u5b9e\u73b0\u200b\u8fd9\u4e00\u200b\u76ee\u6807\u200b\u7684\u200b\u6700\u597d\u200b\u65b9\u6cd5\u200b\u662f\u200b\u4f7f\u200b\u5b83\u200b\u6210\u4e3a\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\uff0c\u200b\u6bcf\u4e2a\u200b\u4eba\u200b\u90fd\u200b\u53ef\u4ee5\u200b\u5728\u200b\u8fd9\u4e9b\u200b\u6761\u6b3e\u200b\u4e0b\u200b\u91cd\u65b0\u200b\u53d1\u5e03\u200b\u548c\u200b\u4fee\u6539\u200b\u3002

\u200b\u8981\u200b\u505a\u5230\u200b\u8fd9\u200b\u4e00\u70b9\u200b\uff0c\u200b\u8bf7\u200b\u5728\u200b\u7a0b\u5e8f\u200b\u4e2d\u200b\u9644\u4e0a\u200b\u4ee5\u4e0b\u200b\u901a\u77e5\u200b\u3002\u200b\u6700\u200b\u5b89\u5168\u200b\u7684\u200b\u505a\u6cd5\u200b\u662f\u200b\u628a\u200b\u5b83\u4eec\u200b\u9644\u5728\u200b\u6bcf\u4e2a\u200b\u6e90\u6587\u4ef6\u200b\u7684\u200b\u5f00\u5934\u200b\uff0c\u200b\u4ee5\u200b\u6700\u200b\u6709\u6548\u200b\u5730\u200b\u8bf4\u660e\u200b\u6392\u9664\u200b\u62c5\u4fdd\u200b\u7684\u200b\u60c5\u51b5\u200b\uff1b\u200b\u6bcf\u4e2a\u200b\u6587\u4ef6\u200b\u81f3\u5c11\u200b\u8981\u200b\u6709\u200b \u201c\u200b\u7248\u6743\u200b\u201d \u200b\u4e00\u884c\u200b\u548c\u200b\u4e00\u4e2a\u200b\u6307\u5411\u200b\u5b8c\u6574\u200b\u901a\u77e5\u200b\u7684\u200b\u6307\u9488\u200b\u3002

Text Only
<one line to give the program's name and a brief idea of what it does.>\nCopyright (C) <year>  <name of author>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU Affero General Public License as\npublished by the Free Software Foundation, either version 3 of the\nLicense, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License\nalong with this program.  If not, see <https://www.gnu.org/licenses/>.\n

\u200b\u7ffb\u8bd1\u200b\uff1a

Text Only
<\u200b\u7528\u200b\u4e00\u884c\u200b\u5b57\u6765\u200b\u8bf4\u660e\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u540d\u79f0\u200b\u548c\u200b\u5b83\u200b\u6240\u200b\u505a\u200b\u7684\u200b\u4e8b\u60c5\u200b\u7684\u200b\u7b80\u5355\u200b\u6982\u5ff5\u200b\u3002>\nCopyright (C) <\u200b\u5e74\u200b> <\u200b\u4f5c\u8005\u59d3\u540d\u200b> \u200b\u7248\u6743\u6240\u6709\u200b\u3002\n\n\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u662f\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\uff1a\u200b\u4f60\u200b\u53ef\u4ee5\u200b\u6839\u636e\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u57fa\u91d1\u4f1a\u200b\u53d1\u5e03\u200b\u7684\u200bGNU Affero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u6761\u6b3e\u200b\uff0c\u200b\u5373\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u7b2c\u200b3\u200b\u7248\u200b\u6216\u200b\uff08\u200b\u60a8\u200b\u9009\u62e9\u200b\u7684\u200b\uff09\u200b\u4efb\u4f55\u200b\u540e\u6765\u200b\u7684\u200b\u7248\u672c\u200b\u91cd\u65b0\u200b\u53d1\u5e03\u200b\u5b83\u200b\u548c\u200b/\u200b\u6216\u200b\u4fee\u6539\u200b\u5b83\u200b\u3002\u3002\n\n\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u53d1\u5e03\u200b\u662f\u200b\u5e0c\u671b\u200b\u5b83\u200b\u80fd\u200b\u8d77\u5230\u200b\u4f5c\u7528\u200b\u3002\u200b\u4f46\u200b\u6ca1\u6709\u200b\u4efb\u4f55\u200b\u4fdd\u8bc1\u200b\uff1b\u200b\u751a\u81f3\u200b\u6ca1\u6709\u200b\u9690\u542b\u200b\u7684\u200b\u4fdd\u8bc1\u200b\u3002\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u5206\u53d1\u200b\u662f\u200b\u5e0c\u671b\u200b\u5b83\u200b\u662f\u200b\u6709\u7528\u200b\u7684\u200b\uff0c\u200b\u4f46\u200b\u6ca1\u6709\u200b\u4efb\u4f55\u200b\u4fdd\u8bc1\u200b\uff0c\u200b\u751a\u81f3\u200b\u6ca1\u6709\u200b\u9690\u542b\u200b\u7684\u200b\u9002\u9500\u5bf9\u8def\u200b\u6216\u200b\u9002\u5408\u200b\u67d0\u4e00\u200b\u7279\u5b9a\u200b\u76ee\u7684\u200b\u7684\u200b\u4fdd\u8bc1\u200b\u3002 \u200b\u53c2\u89c1\u200b GNU Affero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u4e86\u89e3\u200b\u66f4\u200b\u591a\u200b\u7ec6\u8282\u200b\u3002\n\n\u200b\u60a8\u200b\u5e94\u8be5\u200b\u5df2\u7ecf\u200b\u6536\u5230\u200b\u4e86\u200b\u4e00\u4efd\u200bGNU Affero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u526f\u672c\u200b\u3002 \u200b\u5982\u679c\u200b\u6ca1\u6709\u200b\uff0c\u200b\u8bf7\u200b\u53c2\u89c1\u200b<https://www.gnu.org/licenses/>\u3002\n\n\u200b\u8fd8\u8981\u200b\u589e\u52a0\u200b\u5982\u4f55\u200b\u901a\u8fc7\u200b\u7535\u5b50\u200b\u548c\u200b\u7eb8\u8d28\u200b\u90ae\u4ef6\u200b\u4e0e\u200b\u60a8\u200b\u8054\u7cfb\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002\n

\u200b\u5982\u679c\u200b\u60a8\u200b\u7684\u200b\u8f6f\u4ef6\u200b\u53ef\u4ee5\u200b\u901a\u8fc7\u200b\u8ba1\u7b97\u673a\u7f51\u7edc\u200b\u4e0e\u200b\u7528\u6237\u200b\u8fdb\u884c\u200b\u8fdc\u7a0b\u200b\u4ea4\u4e92\u200b\uff0c\u200b\u60a8\u200b\u4e5f\u200b\u5e94\u8be5\u200b\u786e\u4fdd\u200b\u5b83\u200b\u4e3a\u200b\u7528\u6237\u200b\u63d0\u4f9b\u200b\u4e00\u79cd\u200b\u83b7\u5f97\u200b\u5176\u200b\u6e90\u4ee3\u7801\u200b\u7684\u200b\u65b9\u6cd5\u200b\u3002\u200b\u4f8b\u5982\u200b\uff0c\u200b\u5982\u679c\u200b\u60a8\u200b\u7684\u200b\u7a0b\u5e8f\u200b\u662f\u200b\u4e00\u4e2a\u200b\u7f51\u7edc\u5e94\u7528\u200b\u7a0b\u5e8f\u200b\uff0c\u200b\u5b83\u200b\u7684\u200b\u754c\u9762\u200b\u53ef\u4ee5\u200b\u663e\u793a\u200b\u4e00\u4e2a\u200b \u201c\u200b\u6e90\u4ee3\u7801\u200b\u201d \u200b\u7684\u200b\u94fe\u63a5\u200b\uff0c\u200b\u5f15\u5bfc\u200b\u7528\u6237\u200b\u8fdb\u5165\u200b\u4ee3\u7801\u200b\u7684\u200b\u5b58\u6863\u200b\u3002\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u7528\u200b\u5f88\u591a\u200b\u65b9\u6cd5\u200b\u63d0\u4f9b\u200b\u6e90\u7801\u200b\uff0c\u200b\u4e0d\u540c\u200b\u7684\u200b\u89e3\u51b3\u65b9\u6848\u200b\u5bf9\u200b\u4e0d\u540c\u200b\u7684\u200b\u7a0b\u5e8f\u200b\u4f1a\u200b\u66f4\u597d\u200b\uff1b\u200b\u5177\u4f53\u200b\u8981\u6c42\u200b\u89c1\u200b\u7b2c\u200b13\u200b\u8282\u200b\u3002

\u200b\u5982\u679c\u200b\u6709\u200b\u5fc5\u8981\u200b\uff0c\u200b\u60a8\u200b\u8fd8\u200b\u5e94\u8be5\u200b\u8ba9\u200b\u60a8\u200b\u7684\u200b\u96c7\u4e3b\u200b\uff08\u200b\u5982\u679c\u200b\u60a8\u200b\u662f\u200b\u7a0b\u5e8f\u5458\u200b\uff09\u200b\u6216\u200b\u5b66\u6821\u200b\uff08\u200b\u5982\u679c\u200b\u6709\u200b\u7684\u8bdd\u200b\uff09\u200b\u4e3a\u200b\u8be5\u200b\u7a0b\u5e8f\u200b\u7b7e\u7f72\u200b\u4e00\u4efd\u200b \u201c\u200b\u7248\u6743\u200b\u514d\u8d23\u200b\u58f0\u660e\u200b\u201d\u3002\u200b\u6709\u5173\u200b\u8fd9\u65b9\u9762\u200b\u7684\u200b\u66f4\u200b\u591a\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u5982\u4f55\u200b\u7533\u8bf7\u200b\u548c\u200b\u9075\u5b88\u200bGNU AGPL\uff0c\u200b\u8bf7\u200b\u53c2\u89c1\u200bhttps://www.gnu.org/licenses/\u3002

"},{"location":"zh/about/privacy/","title":"Privacy Notice","text":"

\u200b\u7ffb\u8bd1\u200b

\u200b\u672c\u6587\u200b\u5185\u5bb9\u200b\u4e3a\u200b\u673a\u5668\u7ffb\u8bd1\u200b\u7248\u672c\u200b\uff0c\u200b\u65e8\u5728\u200b\u4e3a\u200b\u7528\u6237\u200b\u63d0\u4f9b\u65b9\u4fbf\u200b\u3002 \u200b\u6211\u4eec\u200b\u5df2\u7ecf\u200b\u5c3d\u529b\u200b\u786e\u4fdd\u200b\u7ffb\u8bd1\u200b\u7684\u200b\u51c6\u786e\u6027\u200b\u3002 \u200b\u4f46\u200b\u8bf7\u200b\u6ce8\u610f\u200b\uff0c\u200b\u7ffb\u8bd1\u200b\u5185\u5bb9\u200b\u53ef\u80fd\u200b\u5305\u542b\u200b\u9519\u8bef\u200b\uff0c\u200b\u4ec5\u4f9b\u53c2\u8003\u200b\u3002 \u200b\u8bf7\u4ee5\u200b\u82f1\u6587\u200b\u539f\u6587\u200b\u4e3a\u51c6\u200b\u3002

\u200b\u4e3a\u200b\u6ee1\u8db3\u200b\u5408\u89c4\u6027\u200b\u4e0e\u200b\u6267\u6cd5\u200b\u8981\u6c42\u200b\uff0c\u200b\u7ffb\u8bd1\u200b\u6587\u6863\u200b\u4e2d\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e0d\u200b\u51c6\u786e\u200b\u6216\u200b\u6b67\u4e49\u200b\u4e4b\u5904\u200b\u5747\u200b\u4e0d\u200b\u5177\u6709\u200b\u7ea6\u675f\u529b\u200b\uff0c\u200b\u4e5f\u200b\u4e0d\u200b\u5177\u5907\u200b\u6cd5\u5f8b\u6548\u529b\u200b\u3002

\u200b\u6700\u540e\u200b\u4fee\u8ba2\u200b\u65e5\u671f\u200b

\u200b\u672c\u200b\u58f0\u660e\u200b\u6700\u540e\u200b\u66f4\u65b0\u200b\u4e8e\u200b2024\u200b\u5e74\u200b5\u200b\u6708\u200b4\u200b\u65e5\u200b\u3002

"},{"location":"zh/about/privacy/#_1","title":"\u9690\u79c1\u200b\u58f0\u660e","text":"

\u200b\u672c\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u9002\u7528\u200b\u4e8e\u4e39\u7075\u200b\u56e2\u961f\u200b\uff08\u200b\u4e5f\u200b\u88ab\u79f0\u4f5c\u200b\u4e39\u7075\u200b\uff09\uff08\u200b\u4ee5\u4e0b\u200b\u7b80\u79f0\u200b\u201c\u200b\u6211\u4eec\u200b\u201d\uff09\uff0c\u200b\u63cf\u8ff0\u200b\u4e86\u200b\u5f53\u200b\u60a8\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\uff08\u201c\u200b\u670d\u52a1\u200b\u201d\uff09\u200b\u65f6\u200b\uff0c\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u4ee5\u53ca\u200b\u4e3a\u4f55\u200b\u53ef\u80fd\u200b\u6536\u96c6\u200b\u3001\u200b\u5b58\u50a8\u200b\u3001\u200b\u4f7f\u7528\u200b\u548c\u200b/\u200b\u6216\u200b\u5171\u4eab\u200b\uff08\u201c\u200b\u5904\u7406\u200b\u201d\uff09\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002\u200b\u4f8b\u5982\u200b\u5f53\u200b\u60a8\u200b\uff1a

  • \u200b\u8bbf\u95ee\u200b\u6211\u4eec\u200b\u7684\u200b\u7f51\u7ad9\u200b chanfig.danling.org \u200b\u6216\u200b\u4efb\u4f55\u200b\u94fe\u63a5\u200b\u5230\u200b\u672c\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u7684\u200b\u6211\u4eec\u200b\u7684\u200b\u7f51\u7ad9\u200b\u65f6\u200b

\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u968f\u65f6\u200b\u901a\u8fc7\u200b\u70b9\u51fb\u200b\u4e0b\u9762\u200b\u7684\u200b\u6309\u94ae\u200b\u66f4\u6539\u200b\u60a8\u200b\u7684\u200b\u9690\u79c1\u200b\u8bbe\u7f6e\u200b\uff1a

\u200b\u9690\u79c1\u200b\u63a7\u5236\u200b

\u200b\u6709\u200b\u95ee\u9898\u200b\u6216\u200b\u5173\u6ce8\u200b\uff1f \u200b\u9605\u8bfb\u200b\u672c\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u5c06\u200b\u5e2e\u52a9\u200b\u60a8\u200b\u4e86\u89e3\u200b\u60a8\u200b\u7684\u200b\u9690\u79c1\u200b\u6743\u5229\u200b\u548c\u200b\u9009\u62e9\u200b\u3002 \u200b\u5982\u679c\u200b\u60a8\u200b\u4e0d\u200b\u540c\u610f\u200b\u6211\u4eec\u200b\u7684\u200b\u58f0\u660e\u200b\u548c\u200b\u505a\u6cd5\u200b\uff0c\u200b\u8bf7\u200b\u4e0d\u8981\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u3002 \u200b\u5982\u679c\u200b\u60a8\u200b\u4ecd\u200b\u6709\u200b\u4efb\u4f55\u200b\u95ee\u9898\u200b\u6216\u200b\u5173\u6ce8\u200b\uff0c\u200b\u8bf7\u200b\u901a\u8fc7\u200bprivacy@danling.org\u200b\u4e0e\u200b\u6211\u4eec\u200b\u8054\u7cfb\u200b\u3002

"},{"location":"zh/about/privacy/#0","title":"0. \u200b\u5173\u952e\u70b9\u200b\u603b\u7ed3","text":"

\u200b\u672c\u200b\u603b\u7ed3\u200b\u63d0\u4f9b\u200b\u4e86\u200b\u6211\u4eec\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u7684\u200b\u5173\u952e\u70b9\u200b\uff0c\u200b\u4f46\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u901a\u8fc7\u200b\u70b9\u51fb\u200b\u6bcf\u4e2a\u200b\u5173\u952e\u70b9\u200b\u540e\u200b\u7684\u200b\u94fe\u63a5\u200b\u6216\u200b\u4f7f\u7528\u200b\u76ee\u5f55\u200b\u6765\u200b\u627e\u5230\u200b\u60a8\u200b\u6240\u200b\u67e5\u627e\u200b\u7684\u200b\u90e8\u5206\u200b\u4ee5\u200b\u4e86\u89e3\u200b\u66f4\u200b\u591a\u200b\u8be6\u60c5\u200b\u3002

\u200b\u6211\u4eec\u200b\u5904\u7406\u200b\u54ea\u4e9b\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1f

\u200b\u5f53\u200b\u60a8\u200b\u8bbf\u95ee\u200b\u3001\u200b\u4f7f\u7528\u200b\u6216\u200b\u5bfc\u822a\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u65f6\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u6839\u636e\u200b\u60a8\u200b\u4e0e\u200b\u6211\u4eec\u200b\u4ee5\u53ca\u200b\u670d\u52a1\u200b\u7684\u200b\u4e92\u52a8\u200b\u65b9\u5f0f\u200b\u3001\u200b\u60a8\u200b\u6240\u200b\u505a\u200b\u7684\u200b\u9009\u62e9\u200b\u4ee5\u53ca\u200b\u60a8\u200b\u4f7f\u7528\u200b\u7684\u200b\u4ea7\u54c1\u200b\u548c\u200b\u529f\u80fd\u200b\u6765\u200b\u5904\u7406\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u54ea\u4e9b\u200b\u4fe1\u606f\u200b\uff1f

\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff1f

\u200b\u6211\u4eec\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u4ee5\u200b\u63d0\u4f9b\u200b\u3001\u200b\u6539\u5584\u200b\u548c\u200b\u7ba1\u7406\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\uff0c\u200b\u4e0e\u200b\u60a8\u200b\u6c9f\u901a\u200b\uff0c\u200b\u8fdb\u884c\u200b\u5b89\u5168\u200b\u548c\u200b\u9632\u200b\u6b3a\u8bc8\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u9075\u5b88\u200b\u6cd5\u5f8b\u200b\u3002 \u200b\u6211\u4eec\u200b\u4e5f\u200b\u53ef\u80fd\u200b\u5728\u200b\u5f97\u5230\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u51fa\u4e8e\u200b\u5176\u4ed6\u200b\u76ee\u7684\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002 \u200b\u6211\u4eec\u200b\u4ec5\u200b\u5728\u200b\u6709\u200b\u5408\u6cd5\u200b\u6cd5\u5f8b\u200b\u7406\u7531\u200b\u65f6\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff1f

\u200b\u6211\u4eec\u200b\u5904\u7406\u200b\u4efb\u4f55\u200b\u654f\u611f\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u5417\u200b\uff1f

\u200b\u6211\u4eec\u200b\u4e0d\u200b\u5904\u7406\u200b\u4efb\u4f55\u200b\u654f\u611f\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u4ece\u200b\u7b2c\u4e09\u65b9\u200b\u6536\u96c6\u200b\u4fe1\u606f\u200b\u5417\u200b\uff1f

\u200b\u6211\u4eec\u200b\u4e0d\u200b\u4ece\u200b\u7b2c\u4e09\u65b9\u200b\u6536\u96c6\u200b\u4efb\u4f55\u200b\u4fe1\u606f\u200b\u3002

\u200b\u5728\u200b\u54ea\u4e9b\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u4ee5\u53ca\u200b\u4e0e\u200b\u54ea\u4e9b\u200b\u65b9\u200b\u6211\u4eec\u200b\u5171\u4eab\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1f

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u5728\u200b\u7279\u5b9a\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u4e0e\u200b\u7279\u5b9a\u200b\u7b2c\u4e09\u65b9\u200b\u5171\u4eab\u200b\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u4f55\u65f6\u200b\u4ee5\u53ca\u200b\u4e0e\u200b\u8c01\u200b\u5171\u4eab\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1f

\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u5b89\u5168\u200b\uff1f

\u200b\u6211\u4eec\u200b\u5df2\u7ecf\u200b\u5b9e\u65bd\u200b\u4e86\u200b\u7ec4\u7ec7\u200b\u548c\u200b\u6280\u672f\u200b\u6d41\u7a0b\u200b\u548c\u200b\u7a0b\u5e8f\u200b\u6765\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u4fdd\u6301\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u5b89\u5168\u200b\uff1f

\u200b\u60a8\u200b\u6709\u200b\u54ea\u4e9b\u200b\u6743\u5229\u200b\uff1f

\u200b\u6839\u636e\u200b\u60a8\u200b\u6240\u5728\u200b\u5730\u7406\u4f4d\u7f6e\u200b\uff0c\u200b\u9002\u7528\u200b\u7684\u200b\u9690\u79c1\u200b\u6cd5\u200b\u53ef\u80fd\u200b\u610f\u5473\u7740\u200b\u60a8\u200b\u5bf9\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u6709\u200b\u67d0\u4e9b\u200b\u6743\u5229\u200b\u3002

\u200b\u60a8\u200b\u6709\u200b\u54ea\u4e9b\u200b\u9690\u79c1\u200b\u6743\u5229\u200b\uff1f

\u200b\u60a8\u200b\u5982\u4f55\u200b\u884c\u4f7f\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b\uff1f

\u200b\u884c\u4f7f\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b\u7684\u200b\u6700\u200b\u7b80\u5355\u200b\u65b9\u5f0f\u200b\u662f\u200b\u8054\u7cfb\u200b\u60a8\u200b\u7684\u200b\u53f8\u6cd5\u200b\u7ba1\u8f96\u533a\u200b\u7684\u200b\u76f8\u5173\u200b\u6570\u636e\u4fdd\u62a4\u200b\u76d1\u7ba1\u200b\u673a\u6784\u200b\u3002

\u200b\u5982\u4f55\u200b\u884c\u4f7f\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b\uff1f

"},{"location":"zh/about/privacy/#1","title":"1. \u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u54ea\u4e9b\u200b\u4fe1\u606f\u200b\uff1f","text":""},{"location":"zh/about/privacy/#_2","title":"\u60a8\u200b\u5411\u200b\u6211\u4eec\u200b\u62ab\u9732\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u60a8\u200b\u5411\u200b\u6211\u4eec\u200b\u63d0\u4f9b\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u60a8\u200b\u81ea\u613f\u200b\u5411\u200b\u6211\u4eec\u200b\u63d0\u4f9b\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u5f53\u200b\u60a8\u200b\u8868\u8fbe\u200b\u5bf9\u200b\u6211\u4eec\u200b\u6216\u200b\u6211\u4eec\u200b\u7684\u200b\u4ea7\u54c1\u200b\u548c\u200b\u670d\u52a1\u200b\u7684\u200b\u5174\u8da3\u200b\u3001\u200b\u53c2\u4e0e\u200b\u670d\u52a1\u200b\u4e0a\u200b\u7684\u200b\u6d3b\u52a8\u200b\u6216\u200b\u4ee5\u200b\u5176\u4ed6\u200b\u65b9\u5f0f\u200b\u8054\u7cfb\u200b\u6211\u4eec\u200b\u65f6\u200b\u3002

\u200b\u654f\u611f\u200b\u4e2a\u4eba\u4fe1\u606f\u200b

\u200b\u6211\u4eec\u200b\u4e0d\u200b\u4ece\u200b\u60a8\u200b\u90a3\u91cc\u200b\u6536\u96c6\u200b\u4efb\u4f55\u200b\u654f\u611f\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

"},{"location":"zh/about/privacy/#_3","title":"\u81ea\u52a8\u200b\u6536\u96c6\u200b\u7684\u200b\u4fe1\u606f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u5f53\u200b\u60a8\u200b\u8bbf\u95ee\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u65f6\u200b\uff0c\u200b\u67d0\u4e9b\u200b\u4fe1\u606f\u200b\u2014\u2014\u200b\u5982\u200bIP\u200b\u5730\u5740\u200b\u548c\u200b/\u200b\u6216\u200b\u6d4f\u89c8\u5668\u200b\u548c\u200b\u8bbe\u5907\u200b\u7279\u5f81\u200b\u2014\u2014\u200b\u4f1a\u200b\u81ea\u52a8\u200b\u88ab\u200b\u6536\u96c6\u200b\u3002

\u200b\u6211\u4eec\u200b\u5728\u200b\u60a8\u200b\u8bbf\u95ee\u200b\u3001\u200b\u4f7f\u7528\u200b\u6216\u200b\u5bfc\u822a\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u65f6\u200b\u81ea\u52a8\u200b\u6536\u96c6\u200b\u67d0\u4e9b\u200b\u4fe1\u606f\u200b\u3002 \u200b\u8fd9\u4e9b\u200b\u4fe1\u606f\u200b\u4e0d\u4f1a\u200b\u900f\u9732\u200b\u60a8\u200b\u7684\u200b\u7279\u5b9a\u200b\u8eab\u4efd\u200b\uff08\u200b\u5982\u200b\u60a8\u200b\u7684\u200b\u59d3\u540d\u200b\u6216\u200b\u8054\u7cfb\u200b\u4fe1\u606f\u200b\uff09\uff0c\u200b\u4f46\u200b\u53ef\u80fd\u200b\u5305\u62ec\u200b\u8bbe\u5907\u200b\u548c\u200b\u4f7f\u7528\u200b\u4fe1\u606f\u200b\uff0c\u200b\u5982\u200b\u60a8\u200b\u7684\u200bIP\u200b\u5730\u5740\u200b\u3001\u200b\u6d4f\u89c8\u5668\u200b\u548c\u200b\u8bbe\u5907\u200b\u7279\u6027\u200b\u3001\u200b\u64cd\u4f5c\u7cfb\u7edf\u200b\u3001\u200b\u8bed\u8a00\u200b\u504f\u597d\u200b\u3001\u200b\u5f15\u7528\u200bURL\u3001\u200b\u8bbe\u5907\u200b\u540d\u79f0\u200b\u3001\u200b\u56fd\u5bb6\u200b\u3001\u200b\u4f4d\u7f6e\u200b\u3001\u200b\u6709\u5173\u200b\u60a8\u200b\u5982\u4f55\u200b\u4ee5\u53ca\u200b\u4f55\u65f6\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u5176\u4ed6\u200b\u6280\u672f\u200b\u4fe1\u606f\u200b\u3002 \u200b\u8fd9\u4e9b\u200b\u4fe1\u606f\u200b\u4e3b\u8981\u200b\u662f\u200b\u4e3a\u4e86\u200b\u7ef4\u62a4\u200b\u6211\u4eec\u200b\u670d\u52a1\u200b\u7684\u200b\u5b89\u5168\u6027\u200b\u548c\u200b\u8fd0\u4f5c\u200b\u6240\u200b\u9700\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u6211\u4eec\u200b\u5185\u90e8\u200b\u7684\u200b\u5206\u6790\u200b\u548c\u200b\u62a5\u544a\u200b\u76ee\u7684\u200b\u3002

\u200b\u50cf\u200b\u8bb8\u591a\u200b\u4f01\u4e1a\u200b\u4e00\u6837\u200b\uff0c\u200b\u6211\u4eec\u200b\u8fd8\u200b\u901a\u8fc7\u200bcookies\u200b\u548c\u200b\u7c7b\u4f3c\u200b\u6280\u672f\u200b\u6536\u96c6\u200b\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u7684\u200b\u4fe1\u606f\u200b\u5305\u62ec\u200b\uff1a

  • \u200b\u6807\u8bc6\u7b26\u200b\u3002 \u200b\u6807\u8bc6\u7b26\u200b\u662f\u200b\u5f53\u200b\u60a8\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u65f6\u200b\uff0c\u200b\u6211\u4eec\u200b\u751f\u6210\u200b\u7684\u200b\u7279\u5b9a\u200b\u4e8e\u200b\u8bbe\u5907\u200b\u548c\u200b\u6d4f\u89c8\u5668\u200b\u7684\u200b\u552f\u4e00\u200b\u968f\u673a\u200b\u5b57\u7b26\u4e32\u200b\u3002 \u200b\u8be5\u200b\u6807\u8bc6\u7b26\u200b\u5b58\u50a8\u200b\u5728\u200b\u60a8\u200b\u8bbe\u5907\u200b\u4e0a\u200b\u7684\u200b\u4e00\u4e2a\u200bcookie\u200b\u4e2d\u200b\uff0c\u200b\u4f7f\u200b\u6211\u4eec\u200b\u80fd\u591f\u200b\u5728\u200b\u591a\u4e2a\u200b\u4f1a\u8bdd\u200b\u4e2d\u200b\u4ee5\u53ca\u200b\u60a8\u200b\u8fd4\u56de\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u65f6\u200b\u8bc6\u522b\u200b\u60a8\u200b\u3002 \u200b\u60a8\u200b\u53ef\u4ee5\u200b\u901a\u8fc7\u200b\u6e05\u9664\u200b\u6d4f\u89c8\u5668\u200b\u7f13\u5b58\u200b\u968f\u65f6\u200b\u5220\u9664\u200b\u6b64\u200bcookie\u3002
  • \u200b\u65e5\u5fd7\u200b\u548c\u200b\u4f7f\u7528\u200b\u6570\u636e\u200b\u3002 \u200b\u65e5\u5fd7\u200b\u548c\u200b\u4f7f\u7528\u200b\u6570\u636e\u200b\u662f\u200b\u4e0e\u200b\u670d\u52a1\u200b\u76f8\u5173\u200b\u7684\u200b\u3001\u200b\u8bca\u65ad\u200b\u3001\u200b\u4f7f\u7528\u200b\u548c\u200b\u6027\u80fd\u200b\u4fe1\u606f\u200b\uff0c\u200b\u5f53\u200b\u60a8\u200b\u8bbf\u95ee\u200b\u6216\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u65f6\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u5668\u200b\u4f1a\u200b\u81ea\u52a8\u200b\u6536\u96c6\u200b\uff0c\u200b\u5e76\u200b\u8bb0\u5f55\u200b\u5728\u200b\u65e5\u5fd7\u200b\u6587\u4ef6\u200b\u4e2d\u200b\u3002 \u200b\u6839\u636e\u200b\u60a8\u200b\u4e0e\u200b\u6211\u4eec\u200b\u7684\u200b\u4e92\u52a8\u200b\u65b9\u5f0f\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u65e5\u5fd7\u200b\u6570\u636e\u200b\u53ef\u80fd\u200b\u5305\u62ec\u200b\u60a8\u200b\u7684\u200bIP\u200b\u5730\u5740\u200b\u3001\u200b\u8bbe\u5907\u200b\u4fe1\u606f\u200b\u3001\u200b\u6d4f\u89c8\u5668\u200b\u7c7b\u578b\u200b\u548c\u200b\u8bbe\u7f6e\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u6709\u5173\u200b\u60a8\u200b\u5728\u200b\u670d\u52a1\u200b\u4e2d\u200b\u7684\u200b\u6d3b\u52a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff08\u200b\u5982\u200b\u4e0e\u200b\u60a8\u200b\u4f7f\u7528\u200b\u76f8\u5173\u200b\u7684\u200b\u65e5\u671f\u200b/\u200b\u65f6\u95f4\u200b\u6233\u200b\u3001\u200b\u6d4f\u89c8\u200b\u548c\u200b\u67e5\u770b\u200b\u7684\u200b\u9875\u9762\u200b\u548c\u200b\u6587\u4ef6\u200b\u3001\u200b\u641c\u7d22\u200b\u4ee5\u53ca\u200b\u60a8\u200b\u91c7\u53d6\u200b\u7684\u200b\u5176\u4ed6\u200b\u64cd\u4f5c\u200b\uff0c\u200b\u4f8b\u5982\u200b\u60a8\u200b\u4f7f\u7528\u200b\u7684\u200b\u529f\u80fd\u200b\uff09\uff0c\u200b\u8bbe\u5907\u200b\u4e8b\u4ef6\u200b\u4fe1\u606f\u200b\uff08\u200b\u5982\u200b\u7cfb\u7edf\u6d3b\u52a8\u200b\u3001\u200b\u9519\u8bef\u62a5\u544a\u200b\uff08\u200b\u6709\u65f6\u200b\u79f0\u4e3a\u200b\u2019\u200b\u5d29\u6e83\u200b\u8f6c\u50a8\u200b\u2019\uff09\u200b\u548c\u200b\u786c\u4ef6\u200b\u8bbe\u7f6e\u200b\uff09\u3002
  • \u200b\u8bbe\u5907\u200b\u6570\u636e\u200b\u3002 \u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u8bbe\u5907\u200b\u6570\u636e\u200b\uff0c\u200b\u5982\u200b\u60a8\u200b\u7528\u4e8e\u200b\u8bbf\u95ee\u200b\u670d\u52a1\u200b\u7684\u200b\u8ba1\u7b97\u673a\u200b\u3001\u200b\u7535\u8bdd\u200b\u3001\u200b\u5e73\u677f\u200b\u6216\u200b\u5176\u4ed6\u200b\u8bbe\u5907\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002 \u200b\u6839\u636e\u200b\u6240\u200b\u4f7f\u7528\u200b\u7684\u200b\u8bbe\u5907\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u8bbe\u5907\u200b\u6570\u636e\u200b\u53ef\u80fd\u200b\u5305\u62ec\u200b\u5982\u4e0b\u200b\u4fe1\u606f\u200b\uff1a\u200b\u60a8\u200b\u7684\u200bIP\u200b\u5730\u5740\u200b\uff08\u200b\u6216\u200b\u4ee3\u7406\u670d\u52a1\u5668\u200b\uff09\u3001\u200b\u8bbe\u5907\u200b\u548c\u200b\u5e94\u7528\u7a0b\u5e8f\u200b\u8bc6\u522b\u200b\u53f7\u200b\u3001\u200b\u4f4d\u7f6e\u200b\u3001\u200b\u6d4f\u89c8\u5668\u200b\u7c7b\u578b\u200b\u3001\u200b\u786c\u4ef6\u200b\u578b\u53f7\u200b\u3001\u200b\u4e92\u8054\u7f51\u200b\u670d\u52a1\u63d0\u4f9b\u5546\u200b\u548c\u200b/\u200b\u6216\u200b\u79fb\u52a8\u200b\u8fd0\u8425\u5546\u200b\u3001\u200b\u64cd\u4f5c\u7cfb\u7edf\u200b\u548c\u200b\u7cfb\u7edf\u914d\u7f6e\u200b\u4fe1\u606f\u200b\u3002
  • \u200b\u4f4d\u7f6e\u200b\u6570\u636e\u200b\u3002 \u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u4f4d\u7f6e\u200b\u6570\u636e\u200b\uff0c\u200b\u5982\u200b\u60a8\u200b\u8bbe\u5907\u200b\u7684\u200b\u4f4d\u7f6e\u200b\u4fe1\u606f\u200b\uff0c\u200b\u8fd9\u200b\u53ef\u4ee5\u200b\u662f\u200b\u7cbe\u786e\u200b\u7684\u200b\u4e5f\u200b\u53ef\u4ee5\u200b\u662f\u200b\u4e0d\u200b\u7cbe\u786e\u200b\u7684\u200b\u3002 \u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u591a\u5c11\u200b\u4fe1\u606f\u200b\u53d6\u51b3\u4e8e\u200b\u60a8\u200b\u7528\u4e8e\u200b\u8bbf\u95ee\u200b\u670d\u52a1\u200b\u7684\u200b\u8bbe\u5907\u200b\u7684\u200b\u7c7b\u578b\u200b\u548c\u200b\u8bbe\u7f6e\u200b\u3002 \u200b\u4f8b\u5982\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f7f\u7528\u200bGPS\u200b\u548c\u200b\u5176\u4ed6\u200b\u6280\u672f\u200b\u6765\u200b\u6536\u96c6\u200b\u5730\u7406\u4f4d\u7f6e\u200b\u6570\u636e\u200b\uff0c\u200b\u544a\u8bc9\u200b\u6211\u4eec\u200b\u60a8\u200b\u5f53\u524d\u200b\u7684\u200b\u4f4d\u7f6e\u200b\uff08\u200b\u57fa\u4e8e\u200b\u60a8\u200b\u7684\u200bIP\u200b\u5730\u5740\u200b\uff09\u3002 \u200b\u60a8\u200b\u53ef\u4ee5\u200b\u9009\u62e9\u200b\u4e0d\u8ba9\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u6b64\u200b\u4fe1\u606f\u200b\uff0c\u200b\u65b9\u6cd5\u200b\u662f\u200b\u62d2\u7edd\u200b\u8bbf\u95ee\u4fe1\u606f\u200b\u6216\u200b\u5728\u200b\u60a8\u200b\u7684\u200b\u8bbe\u5907\u200b\u4e0a\u200b\u7981\u7528\u200b\u4f4d\u7f6e\u200b\u8bbe\u7f6e\u200b\u3002
"},{"location":"zh/about/privacy/#_4","title":"\u6211\u4eec\u200b\u6536\u96c6\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7c7b\u522b","text":"

\u200b\u8fc7\u53bb\u200b\u5341\u4e8c\u200b\uff0812\uff09\u200b\u4e2a\u200b\u6708\u200b\u5185\u200b\uff0c\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u4e86\u200b\u4ee5\u4e0b\u200b\u7c7b\u522b\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1a

\u200b\u7c7b\u522b\u200b \u200b\u793a\u4f8b\u200b \u200b\u5df2\u200b\u6536\u96c6\u200b A. \u200b\u6807\u8bc6\u7b26\u200b \u200b\u8054\u7cfb\u65b9\u5f0f\u200b\uff0c\u200b\u5982\u200b\u771f\u5b9e\u200b\u59d3\u540d\u200b\u3001\u200b\u522b\u540d\u200b\u3001\u200b\u90ae\u653f\u200b\u5730\u5740\u200b\u3001\u200b\u7535\u8bdd\u200b\u6216\u200b\u79fb\u52a8\u200b\u8054\u7cfb\u200b\u53f7\u7801\u200b\u3001\u200b\u72ec\u7279\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6807\u8bc6\u7b26\u200b\u3001\u200b\u5728\u7ebf\u200b\u6807\u8bc6\u7b26\u200b\u3001\u200b\u4e92\u8054\u7f51\u534f\u8bae\u200b\u5730\u5740\u200b\u3001\u200b\u7535\u5b50\u90ae\u4ef6\u200b\u5730\u5740\u200b\u548c\u200b\u5e10\u6237\u200b\u540d\u79f0\u200b \u200b\u662f\u200b B. \u200b\u52a0\u5229\u798f\u5c3c\u4e9a\u200b\u5ba2\u6237\u200b\u8bb0\u5f55\u200b\u6cd5\u4e2d\u200b\u5b9a\u4e49\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b \u200b\u59d3\u540d\u200b\u3001\u200b\u8054\u7cfb\u200b\u4fe1\u606f\u200b\u3001\u200b\u6559\u80b2\u200b\u3001\u200b\u5c31\u4e1a\u200b\u3001\u200b\u5c31\u4e1a\u200b\u5386\u53f2\u200b\u548c\u200b\u8d22\u52a1\u200b\u4fe1\u606f\u200b \u200b\u5426\u200b C. \u200b\u5dde\u200b\u6216\u200b\u8054\u90a6\u200b\u6cd5\u5f8b\u200b\u4e0b\u200b\u7684\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u5206\u7c7b\u200b\u7279\u5f81\u200b \u200b\u6027\u522b\u200b\u3001\u200b\u5e74\u9f84\u200b\u3001\u200b\u51fa\u751f\u65e5\u671f\u200b\u3001\u200b\u79cd\u65cf\u200b\u548c\u200b\u6c11\u65cf\u200b\u3001\u200b\u56fd\u7c4d\u200b\u3001\u200b\u5a5a\u59fb\u72b6\u51b5\u200b\u548c\u200b\u5176\u4ed6\u200b\u4eba\u53e3\u200b\u7edf\u8ba1\u6570\u636e\u200b \u200b\u5426\u200b D. \u200b\u5546\u4e1a\u4fe1\u606f\u200b \u200b\u4ea4\u6613\u200b\u4fe1\u606f\u200b\u3001\u200b\u8d2d\u4e70\u200b\u5386\u53f2\u200b\u3001\u200b\u8d22\u52a1\u200b\u8be6\u7ec6\u4fe1\u606f\u200b\u548c\u200b\u652f\u4ed8\u200b\u4fe1\u606f\u200b \u200b\u5426\u200b E. \u200b\u751f\u7269\u200b\u8bc6\u522b\u200b\u4fe1\u606f\u200b \u200b\u6307\u7eb9\u200b\u548c\u200b\u58f0\u7eb9\u200b \u200b\u5426\u200b F. \u200b\u4e92\u8054\u7f51\u200b\u6216\u200b\u5176\u4ed6\u200b\u7c7b\u4f3c\u200b\u7f51\u7edc\u200b\u6d3b\u52a8\u200b \u200b\u6d4f\u89c8\u200b\u5386\u53f2\u200b\u3001\u200b\u641c\u7d22\u200b\u5386\u53f2\u200b\u3001\u200b\u5728\u7ebf\u200b\u884c\u4e3a\u200b\u3001\u200b\u5174\u8da3\u200b\u6570\u636e\u200b\u548c\u200b\u4e0e\u200b\u6211\u4eec\u200b\u548c\u200b\u5176\u4ed6\u200b\u7f51\u7ad9\u200b\u3001\u200b\u5e94\u7528\u7a0b\u5e8f\u200b\u3001\u200b\u7cfb\u7edf\u200b\u548c\u200b\u5e7f\u544a\u200b\u7684\u200b\u4e92\u52a8\u200b \u200b\u662f\u200b G. \u200b\u5730\u7406\u4f4d\u7f6e\u200b\u6570\u636e\u200b \u200b\u8bbe\u5907\u200b\u4f4d\u7f6e\u200b \u200b\u662f\u200b H. \u200b\u97f3\u9891\u200b\u3001\u200b\u7535\u5b50\u200b\u3001\u200b\u611f\u89c9\u200b\u6216\u200b\u7c7b\u4f3c\u200b\u4fe1\u606f\u200b \u200b\u5728\u200b\u6211\u4eec\u200b\u7684\u200b\u4e1a\u52a1\u200b\u6d3b\u52a8\u200b\u4e2d\u200b\u521b\u5efa\u200b\u7684\u200b\u56fe\u50cf\u200b\u548c\u200b\u97f3\u9891\u200b\u3001\u200b\u89c6\u9891\u200b\u6216\u200b\u901a\u8bdd\u5f55\u97f3\u200b \u200b\u5426\u200b I. \u200b\u4e0e\u200b\u804c\u4e1a\u200b\u76f8\u5173\u200b\u7684\u200b\u4fe1\u606f\u200b \u200b\u4e3a\u4e86\u200b\u5728\u200b\u4e1a\u52a1\u200b\u5c42\u9762\u200b\u63d0\u4f9b\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u800c\u200b\u6536\u96c6\u200b\u7684\u200b\u5546\u4e1a\u200b\u8054\u7cfb\u200b\u4fe1\u606f\u200b\u6216\u200b\u804c\u52a1\u200b\u540d\u79f0\u200b\u3001\u200b\u5de5\u4f5c\u200b\u5386\u53f2\u200b\u548c\u200b\u804c\u4e1a\u8d44\u683c\u200b \u200b\u5426\u200b J. \u200b\u6559\u80b2\u200b\u4fe1\u606f\u200b \u200b\u5b66\u751f\u200b\u8bb0\u5f55\u200b\u548c\u200b\u76ee\u5f55\u200b\u4fe1\u606f\u200b \u200b\u5426\u200b K. \u200b\u4ece\u200b\u6536\u96c6\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u4e2d\u200b\u63a8\u65ad\u51fa\u200b\u7684\u200b\u63a8\u8bba\u200b \u200b\u4ece\u200b\u4e0a\u8ff0\u200b\u4efb\u4f55\u200b\u6536\u96c6\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u4e2d\u200b\u63a8\u65ad\u51fa\u200b\u7684\u200b\u7528\u4e8e\u200b\u521b\u5efa\u200b\u4e2a\u4eba\u200b\u504f\u597d\u200b\u548c\u200b\u7279\u5f81\u200b\u7684\u200b\u6982\u51b5\u200b\u6216\u200b\u6458\u8981\u200b \u200b\u662f\u200b L. \u200b\u654f\u611f\u200b\u4e2a\u4eba\u4fe1\u606f\u200b \u200b\u5426\u200b

\u200b\u6211\u4eec\u200b\u8fd8\u200b\u53ef\u80fd\u200b\u5728\u200b\u60a8\u200b\u4e0e\u200b\u6211\u4eec\u200b\u4eb2\u81ea\u200b\u3001\u200b\u5728\u7ebf\u200b\u6216\u200b\u901a\u8fc7\u200b\u7535\u8bdd\u200b\u6216\u200b\u90ae\u4ef6\u200b\u4e0e\u200b\u6211\u4eec\u200b\u4e92\u52a8\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u6536\u96c6\u200b\u5176\u4ed6\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u5305\u62ec\u200b\uff1a

  • \u200b\u901a\u8fc7\u200b\u6211\u4eec\u200b\u7684\u200b\u5ba2\u6237\u200b\u652f\u6301\u200b\u6e20\u9053\u200b\u83b7\u5f97\u200b\u5e2e\u52a9\u200b\uff1b
  • \u200b\u53c2\u4e0e\u200b\u5ba2\u6237\u200b\u8c03\u67e5\u200b\u6216\u200b\u7ade\u8d5b\u200b\uff1b\u200b\u4ee5\u53ca\u200b
  • \u200b\u4fc3\u8fdb\u200b\u6211\u4eec\u200b\u670d\u52a1\u200b\u7684\u200b\u4ea4\u4ed8\u200b\u5e76\u200b\u56de\u5e94\u200b\u60a8\u200b\u7684\u200b\u67e5\u8be2\u200b\u3002

\u200b\u6211\u4eec\u200b\u5c06\u200b\u6839\u636e\u200b\u9700\u8981\u200b\u5728\u200b\u4ee5\u4e0b\u200b\u671f\u9650\u5185\u200b\u4f7f\u7528\u200b\u548c\u200b\u4fdd\u7559\u200b\u6240\u200b\u6536\u96c6\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u4ee5\u4fbf\u200b\u4e3a\u200b\u60a8\u200b\u63d0\u4f9b\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\uff0c\u200b\u5e76\u200b\u6839\u636e\u200b\u9700\u8981\u200b\u9075\u5b88\u200b\u6211\u4eec\u200b\u7684\u200b\u6cd5\u5f8b\u4e49\u52a1\u200b\u3001\u200b\u89e3\u51b3\u200b\u4e89\u8bae\u200b\u548c\u200b\u6267\u884c\u200b\u6211\u4eec\u200b\u7684\u200b\u534f\u8bae\u200b\uff1a

  • A \u200b\u7c7b\u200b\uff1a\u200b\u65e0\u9650\u671f\u200b
  • F \u200b\u7c7b\u200b\uff1a\u200b\u65e0\u9650\u671f\u200b
  • G \u200b\u7c7b\u200b\uff1a\u200b\u65e0\u9650\u671f\u200b
  • K \u200b\u7c7b\u200b\uff1a\u200b\u65e0\u9650\u671f\u200b
"},{"location":"zh/about/privacy/#2","title":"2. \u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u4ee5\u200b\u63d0\u4f9b\u200b\u3001\u200b\u6539\u5584\u200b\u548c\u200b\u7ba1\u7406\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\uff0c\u200b\u4e0e\u200b\u60a8\u200b\u6c9f\u901a\u200b\uff0c\u200b\u8fdb\u884c\u200b\u5b89\u5168\u200b\u548c\u200b\u9632\u200b\u6b3a\u8bc8\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u9075\u5b88\u200b\u6cd5\u5f8b\u200b\u3002 \u200b\u6211\u4eec\u200b\u4e5f\u200b\u53ef\u80fd\u200b\u5728\u200b\u5f97\u5230\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u51fa\u4e8e\u200b\u5176\u4ed6\u200b\u76ee\u7684\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u51fa\u4e8e\u200b\u591a\u79cd\u200b\u539f\u56e0\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u8fd9\u200b\u53d6\u51b3\u4e8e\u200b\u60a8\u200b\u5982\u4f55\u200b\u4e0e\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u4e92\u52a8\u200b\uff0c\u200b\u5305\u62ec\u200b\uff1a

  • \u200b\u4fdd\u62a4\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u4f5c\u4e3a\u200b\u6211\u4eec\u200b\u4fdd\u6301\u200b\u670d\u52a1\u200b\u5b89\u5168\u200b\u7684\u200b\u52aa\u529b\u200b\u7684\u200b\u4e00\u90e8\u5206\u200b\uff0c\u200b\u5305\u62ec\u200b\u76d1\u63a7\u200b\u548c\u200b\u9884\u9632\u200b\u6b3a\u8bc8\u200b\u3002
  • \u200b\u8bc6\u522b\u200b\u7528\u6237\u200b\u8d8b\u52bf\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5904\u7406\u200b\u6709\u5173\u200b\u60a8\u200b\u5982\u4f55\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4ee5\u200b\u66f4\u597d\u200b\u5730\u200b\u4e86\u89e3\u200b\u5b83\u4eec\u200b\u7684\u200b\u4f7f\u7528\u200b\u60c5\u51b5\u200b\uff0c\u200b\u4ece\u800c\u200b\u6539\u8fdb\u200b\u5b83\u4eec\u200b\u3002
  • \u200b\u4fdd\u5b58\u200b\u6216\u200b\u4fdd\u62a4\u200b\u4e2a\u4eba\u200b\u7684\u200b\u91cd\u8981\u200b\u5229\u76ca\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5728\u200b\u5fc5\u8981\u200b\u65f6\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4ee5\u200b\u4fdd\u5b58\u200b\u6216\u200b\u4fdd\u62a4\u200b\u4e2a\u4eba\u200b\u7684\u200b\u91cd\u8981\u200b\u5229\u76ca\u200b\uff0c\u200b\u4f8b\u5982\u200b\u4e3a\u4e86\u200b\u9632\u6b62\u200b\u4f24\u5bb3\u200b\u3002
"},{"location":"zh/about/privacy/#3","title":"3. \u200b\u6211\u4eec\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u6709\u200b\u4ec0\u4e48\u200b\u6cd5\u5f8b\u4f9d\u636e\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u53ea\u6709\u200b\u5728\u200b\u6211\u4eec\u200b\u8ba4\u4e3a\u200b\u5fc5\u8981\u200b\u4e14\u200b\u6709\u200b\u6709\u6548\u200b\u7684\u200b\u6cd5\u5f8b\u200b\u7406\u7531\u200b\uff08\u200b\u5373\u200b\u6cd5\u5f8b\u4f9d\u636e\u200b\uff09\u200b\u65f6\u624d\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u5982\u200b\u4e0e\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u3001\u200b\u9075\u5b88\u200b\u6cd5\u5f8b\u200b\u3001\u200b\u63d0\u4f9b\u200b\u670d\u52a1\u200b\u7ed9\u200b\u60a8\u200b\u8fdb\u5165\u200b\u6216\u200b\u5c65\u884c\u200b\u6211\u4eec\u200b\u7684\u200b\u5408\u540c\u200b\u4e49\u52a1\u200b\u3001\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b\u6216\u200b\u6ee1\u8db3\u200b\u6211\u4eec\u200b\u5408\u6cd5\u200b\u7684\u200b\u4e1a\u52a1\u200b\u5229\u76ca\u200b\u3002

\u200b\u6b27\u76df\u200b\u901a\u7528\u200b\u6570\u636e\u4fdd\u62a4\u200b\u6761\u4f8b\u200b\uff08GDPR\uff09\u200b\u548c\u200b\u82f1\u56fd\u200bGDPR\u200b\u8981\u6c42\u200b\u6211\u4eec\u200b\u89e3\u91ca\u200b\u6211\u4eec\u200b\u4f9d\u9760\u200b\u7684\u200b\u6709\u6548\u200b\u6cd5\u5f8b\u4f9d\u636e\u200b\u4ee5\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002 \u200b\u56e0\u6b64\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f9d\u8d56\u200b\u4ee5\u4e0b\u200b\u6cd5\u5f8b\u4f9d\u636e\u200b\u6765\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1a

  • \u200b\u540c\u610f\u200b\u3002 \u200b\u5982\u679c\u200b\u60a8\u200b\u5df2\u200b\u7ed9\u200b\u6211\u4eec\u200b\u660e\u786e\u200b\u540c\u610f\u200b\u4f7f\u7528\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7528\u4e8e\u200b\u67d0\u4e2a\u200b\u7279\u5b9a\u200b\u76ee\u7684\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002 \u200b\u60a8\u200b\u6709\u6743\u200b\u968f\u65f6\u200b\u64a4\u56de\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u3002 \u200b\u4e86\u89e3\u200b\u66f4\u200b\u591a\u200b\u5173\u4e8e\u200b\u64a4\u56de\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u3002
  • \u200b\u5408\u6cd5\u5229\u76ca\u200b\u3002 \u200b\u5f53\u200b\u6211\u4eec\u200b\u8ba4\u4e3a\u200b\u51fa\u4e8e\u200b\u6211\u4eec\u200b\u5408\u6cd5\u200b\u7684\u200b\u4e1a\u52a1\u200b\u5229\u76ca\u200b\u6765\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u662f\u200b\u5408\u7406\u200b\u5fc5\u8981\u200b\u7684\u200b\uff0c\u200b\u5e76\u4e14\u200b\u8fd9\u4e9b\u200b\u5229\u76ca\u200b\u4e0d\u200b\u8d85\u8fc7\u200b\u60a8\u200b\u7684\u200b\u5229\u76ca\u200b\u548c\u200b\u57fa\u672c\u6743\u5229\u200b\u4e0e\u200b\u81ea\u7531\u200b\u65f6\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002 \u200b\u4f8b\u5982\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u7528\u4e8e\u200b\uff1a
    • \u200b\u5206\u6790\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u5982\u4f55\u200b\u88ab\u200b\u4f7f\u7528\u200b\uff0c\u200b\u4ee5\u4fbf\u200b\u6211\u4eec\u200b\u53ef\u4ee5\u200b\u6539\u8fdb\u200b\u5b83\u4eec\u200b\u4ee5\u200b\u5438\u5f15\u200b\u548c\u200b\u4fdd\u7559\u200b\u7528\u6237\u200b
    • \u200b\u8bca\u65ad\u200b\u95ee\u9898\u200b\u548c\u200b/\u200b\u6216\u200b\u9884\u9632\u200b\u6b3a\u8bc8\u200b\u6d3b\u52a8\u200b
  • \u200b\u6cd5\u5f8b\u4e49\u52a1\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5728\u200b\u6211\u4eec\u200b\u8ba4\u4e3a\u200b\u5fc5\u987b\u200b\u9075\u5b88\u200b\u6211\u4eec\u200b\u7684\u200b\u6cd5\u5f8b\u4e49\u52a1\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4f8b\u5982\u200b\u4e0e\u200b\u6267\u6cd5\u200b\u673a\u6784\u200b\u6216\u200b\u76d1\u7ba1\u200b\u673a\u6784\u200b\u5408\u4f5c\u200b\u3001\u200b\u884c\u4f7f\u200b\u6216\u200b\u634d\u536b\u200b\u6211\u4eec\u200b\u7684\u200b\u6cd5\u5f8b\u200b\u6743\u5229\u200b\uff0c\u200b\u6216\u200b\u5728\u200b\u6211\u4eec\u200b\u53c2\u4e0e\u200b\u7684\u200b\u8bc9\u8bbc\u200b\u4e2d\u200b\u62ab\u9732\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u4f5c\u4e3a\u200b\u8bc1\u636e\u200b\u3002
  • \u200b\u91cd\u8981\u200b\u5229\u76ca\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5728\u200b\u6211\u4eec\u200b\u8ba4\u4e3a\u200b\u5fc5\u987b\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u6216\u200b\u7b2c\u4e09\u65b9\u200b\u7684\u200b\u91cd\u8981\u200b\u5229\u76ca\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4f8b\u5982\u200b\u6d89\u53ca\u200b\u6f5c\u5728\u200b\u5a01\u80c1\u200b\u4efb\u4f55\u4eba\u200b\u7684\u200b\u5b89\u5168\u200b\u7684\u200b\u60c5\u51b5\u200b\u3002

\u200b\u5728\u200b\u52a0\u62ff\u5927\u200b\u5904\u7406\u200b\u7684\u200b\u540c\u610f\u200b

\u200b\u5982\u679c\u200b\u60a8\u200b\u4f4d\u4e8e\u200b\u52a0\u62ff\u5927\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u5728\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u4e0b\u200b\u5728\u200b\u67d0\u4e9b\u200b\u7279\u6b8a\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u65e0\u9700\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u5c31\u200b\u53ef\u4ee5\u200b\u5408\u6cd5\u200b\u5730\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u5305\u62ec\u200b\u4f8b\u5982\u200b\uff1a

  • \u200b\u5982\u679c\u200b\u6536\u96c6\u200b\u660e\u663e\u200b\u7b26\u5408\u200b\u4e2a\u4eba\u200b\u7684\u200b\u5229\u76ca\u200b\u4e14\u200b\u65e0\u6cd5\u200b\u53ca\u65f6\u200b\u83b7\u5f97\u200b\u540c\u610f\u200b
  • \u200b\u7528\u4e8e\u200b\u8c03\u67e5\u200b\u548c\u200b\u6b3a\u8bc8\u200b\u68c0\u6d4b\u200b\u4e0e\u200b\u9884\u9632\u200b
  • \u200b\u7528\u4e8e\u200b\u5546\u4e1a\u200b\u4ea4\u6613\u200b\uff0c\u200b\u524d\u63d0\u200b\u662f\u200b\u6ee1\u8db3\u200b\u67d0\u4e9b\u200b\u6761\u4ef6\u200b
  • \u200b\u5982\u679c\u200b\u4fe1\u606f\u200b\u5305\u542b\u200b\u5728\u200b\u8bc1\u4eba\u200b\u58f0\u660e\u200b\u4e2d\u200b\uff0c\u200b\u4e14\u200b\u6536\u96c6\u200b\u5bf9\u4e8e\u200b\u8bc4\u4f30\u200b\u3001\u200b\u5904\u7406\u200b\u6216\u200b\u89e3\u51b3\u200b\u4fdd\u9669\u200b\u7d22\u8d54\u200b\u662f\u200b\u5fc5\u8981\u200b\u7684\u200b
  • \u200b\u7528\u4e8e\u200b\u8bc6\u522b\u200b\u53d7\u4f24\u200b\u3001\u200b\u751f\u75c5\u200b\u6216\u200b\u5df2\u6545\u200b\u4eba\u58eb\u200b\u5e76\u200b\u4e0e\u200b\u8fd1\u4eb2\u200b\u6c9f\u901a\u200b
  • \u200b\u5982\u679c\u200b\u6211\u4eec\u200b\u6709\u200b\u5408\u7406\u200b\u7684\u200b\u7406\u7531\u200b\u76f8\u4fe1\u200b\u67d0\u4e2a\u200b\u4eba\u200b\u5df2\u7ecf\u200b\u3001\u200b\u6b63\u5728\u200b\u6216\u200b\u53ef\u80fd\u200b\u6210\u4e3a\u200b\u91d1\u878d\u200b\u6ee5\u7528\u200b\u7684\u200b\u53d7\u5bb3\u8005\u200b
  • \u200b\u5982\u679c\u200b\u5408\u7406\u200b\u9884\u671f\u200b\u901a\u8fc7\u200b\u5f81\u5f97\u200b\u540c\u610f\u200b\u4ee5\u200b\u6536\u96c6\u200b\u548c\u200b\u4f7f\u7528\u200b\u4fe1\u606f\u200b\u4f1a\u200b\u635f\u5bb3\u200b\u4fe1\u606f\u200b\u7684\u200b\u53ef\u7528\u6027\u200b\u6216\u200b\u51c6\u786e\u6027\u200b\uff0c\u200b\u5e76\u4e14\u200b\u6536\u96c6\u200b\u5bf9\u4e8e\u200b\u8c03\u67e5\u200b\u8fdd\u53cd\u200b\u534f\u8bae\u200b\u6216\u200b\u8fdd\u53cd\u200b\u52a0\u62ff\u5927\u200b\u6216\u7701\u200b\u6cd5\u5f8b\u200b\u7684\u200b\u76ee\u7684\u200b\u662f\u200b\u5408\u7406\u200b\u7684\u200b
  • \u200b\u5982\u679c\u200b\u62ab\u9732\u200b\u662f\u200b\u4e3a\u4e86\u200b\u9075\u5b88\u200b\u4f20\u7968\u200b\u3001\u200b\u641c\u67e5\u200b\u4ee4\u200b\u3001\u200b\u6cd5\u9662\u200b\u547d\u4ee4\u200b\u6216\u200b\u4e0e\u200b\u8bb0\u5f55\u200b\u751f\u4ea7\u200b\u76f8\u5173\u200b\u7684\u200b\u6cd5\u9662\u200b\u89c4\u5219\u200b
  • \u200b\u5982\u679c\u200b\u4fe1\u606f\u200b\u662f\u200b\u7531\u200b\u4e2a\u4eba\u200b\u5728\u200b\u5176\u200b\u5c31\u4e1a\u200b\u3001\u200b\u4e1a\u52a1\u200b\u6216\u200b\u4e13\u4e1a\u200b\u8fc7\u7a0b\u200b\u4e2d\u200b\u4ea7\u751f\u200b\u7684\u200b\uff0c\u200b\u5e76\u4e14\u200b\u6536\u96c6\u200b\u4e0e\u200b\u4fe1\u606f\u200b\u4ea7\u751f\u200b\u7684\u200b\u76ee\u7684\u200b\u4e00\u81f4\u200b
  • \u200b\u5982\u679c\u200b\u6536\u96c6\u200b\u4ec5\u200b\u7528\u4e8e\u200b\u65b0\u95fb\u200b\u3001\u200b\u827a\u672f\u200b\u6216\u200b\u6587\u5b66\u200b\u76ee\u7684\u200b
  • \u200b\u5982\u679c\u200b\u4fe1\u606f\u200b\u662f\u200b\u516c\u5f00\u200b\u53ef\u7528\u200b\u7684\u200b\uff0c\u200b\u5e76\u4e14\u200b\u901a\u8fc7\u200b\u89c4\u5b9a\u200b\u6307\u5b9a\u200b
"},{"location":"zh/about/privacy/#4","title":"4. \u200b\u6211\u4eec\u200b\u4f55\u65f6\u200b\u4ee5\u53ca\u200b\u4e0e\u200b\u8c01\u200b\u5171\u4eab\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u5728\u200b\u672c\u8282\u200b\u63cf\u8ff0\u200b\u7684\u200b\u7279\u5b9a\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u548c\u200b/\u200b\u6216\u200b\u4e0e\u200b\u4ee5\u4e0b\u200b\u7b2c\u4e09\u65b9\u200b\u5171\u4eab\u200b\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5c06\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7528\u4e8e\u200b\u6211\u4eec\u200b\u7684\u200b\u4e1a\u52a1\u200b\u76ee\u7684\u200b\uff0c\u200b\u5982\u200b\u8fdb\u884c\u200b\u5185\u90e8\u200b\u7814\u7a76\u200b\u4ee5\u200b\u8fdb\u884c\u200b\u6280\u672f\u5f00\u53d1\u200b\u548c\u200b\u5c55\u793a\u200b\u3002 \u200b\u8fd9\u200b\u4e0d\u200b\u88ab\u200b\u89c6\u4e3a\u200b\u201c\u200b\u51fa\u552e\u200b\u201d\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

\u200b\u4f9b\u5e94\u5546\u200b\u3001\u200b\u987e\u95ee\u200b\u548c\u200b\u5176\u4ed6\u200b\u7b2c\u4e09\u65b9\u200b\u670d\u52a1\u63d0\u4f9b\u5546\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u4e0e\u200b\u4e3a\u200b\u6211\u4eec\u200b\u670d\u52a1\u200b\u6216\u200b\u4ee3\u8868\u200b\u6211\u4eec\u200b\u5de5\u4f5c\u200b\u5e76\u200b\u9700\u8981\u200b\u8bbf\u95ee\u200b\u6b64\u7c7b\u200b\u4fe1\u606f\u200b\u4ee5\u200b\u6267\u884c\u200b\u8be5\u200b\u5de5\u4f5c\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u4f9b\u5e94\u5546\u200b\u3001\u200b\u670d\u52a1\u63d0\u4f9b\u5546\u200b\u3001\u200b\u627f\u5305\u5546\u200b\u6216\u200b\u4ee3\u7406\u200b\uff08\u201c\u200b\u7b2c\u4e09\u65b9\u200b\u201d\uff09\u200b\u5171\u4eab\u200b\u60a8\u200b\u7684\u200b\u6570\u636e\u200b\u3002 \u200b\u6211\u4eec\u200b\u4e0e\u200b\u6211\u4eec\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u7b7e\u8ba2\u200b\u4e86\u200b\u5408\u540c\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u5408\u540c\u200b\u65e8\u5728\u200b\u5e2e\u52a9\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002 \u200b\u8fd9\u200b\u610f\u5473\u7740\u200b\u4ed6\u4eec\u200b\u4e0d\u80fd\u200b\u5728\u200b\u672a\u7ecf\u200b\u6211\u4eec\u200b\u6307\u793a\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u505a\u200b\u4efb\u4f55\u200b\u4e8b\u60c5\u200b\u3002 \u200b\u4ed6\u4eec\u200b\u4e5f\u200b\u4e0d\u4f1a\u200b\u4e0e\u200b\u6211\u4eec\u200b\u4ee5\u5916\u200b\u7684\u200b\u4efb\u4f55\u200b\u7ec4\u7ec7\u200b\u5171\u4eab\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002 \u200b\u4ed6\u4eec\u200b\u8fd8\u200b\u627f\u8bfa\u200b\u4fdd\u62a4\u200b\u4ed6\u4eec\u200b\u4ee3\u8868\u200b\u6211\u4eec\u200b\u6301\u6709\u200b\u7684\u200b\u6570\u636e\u200b\u5e76\u200b\u6309\u7167\u200b\u6211\u4eec\u200b\u7684\u200b\u6307\u793a\u200b\u4fdd\u7559\u200b\u8be5\u200b\u6570\u636e\u200b\u3002

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u5171\u4eab\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u5982\u4e0b\u200b\uff1a

  • \u200b\u5e7f\u544a\u200b\u3001\u200b\u76f4\u9500\u200b\u548c\u200b\u6f5c\u5728\u200b\u5ba2\u6237\u200b\u751f\u6210\u200b
    • Google AdSense
  • \u200b\u4e91\u200b\u8ba1\u7b97\u200b\u670d\u52a1\u200b
    • Microsoft Azure
    • Amazon Web Services (AWS)
    • Google Cloud Platform (GCP)
  • \u200b\u901a\u4fe1\u200b\u548c\u200b\u5185\u5bb9\u200b\u4ea4\u4ed8\u200b\u7f51\u7edc\u200b (CDN) \u200b\u670d\u52a1\u200b
    • Cloudflare
  • \u200b\u5185\u5bb9\u200b\u4f18\u5316\u200b
    • Google\u200b\u7ad9\u70b9\u200b\u641c\u7d22\u200b
    • Google\u200b\u5b57\u4f53\u200b
  • \u200b\u529f\u80fd\u200b\u548c\u200b\u57fa\u7840\u8bbe\u65bd\u200b\u4f18\u5316\u200b
    • GitHub\u200b\u9875\u9762\u200b
  • \u200b\u7528\u6237\u200b\u8bc4\u8bba\u200b\u548c\u200b\u8bba\u575b\u200b
    • Disqus
    • GitHub\u200b\u8bae\u9898\u200b
    • GitHub\u200b\u8ba8\u8bba\u200b
  • \u200b\u7f51\u7edc\u200b\u548c\u200b\u79fb\u52a8\u200b\u5206\u6790\u200b
    • Google Analytics

\u200b\u6211\u4eec\u200b\u8fd8\u200b\u53ef\u80fd\u200b\u9700\u8981\u200b\u5728\u200b\u4ee5\u4e0b\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u5171\u4eab\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1a

  • \u200b\u4e1a\u52a1\u200b\u8f6c\u79fb\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u5728\u200b\u8fdb\u884c\u200b\u4efb\u4f55\u200b\u5e76\u8d2d\u200b\u3001\u200b\u51fa\u552e\u200b\u516c\u53f8\u200b\u8d44\u4ea7\u200b\u3001\u200b\u878d\u8d44\u200b\u6216\u200b\u6536\u8d2d\u200b\u6211\u4eec\u200b\u5168\u90e8\u200b\u6216\u200b\u90e8\u5206\u200b\u4e1a\u52a1\u200b\u7684\u200b\u8c08\u5224\u200b\u4e2d\u200b\u5171\u4eab\u200b\u6216\u200b\u8f6c\u8ba9\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002

\u200b\u8fc7\u53bb\u200b\u5341\u4e8c\u200b\uff0812\uff09\u200b\u4e2a\u200b\u6708\u200b\u6211\u4eec\u200b\u51fa\u4e8e\u200b\u4e1a\u52a1\u200b\u76ee\u7684\u200b\u62ab\u9732\u200b\u4e86\u200b\u4ee5\u4e0b\u200b\u7c7b\u522b\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1a

\u200b\u65e0\u200b

\u200b\u8fc7\u53bb\u200b\u5341\u4e8c\u200b\uff0812\uff09\u200b\u4e2a\u200b\u6708\u200b\u6211\u4eec\u200b\u51fa\u552e\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u7c7b\u522b\u200b\uff1a

\u200b\u65e0\u200b

\u200b\u8fc7\u53bb\u200b\u5341\u4e8c\u200b\uff0812\uff09\u200b\u4e2a\u200b\u6708\u200b\u6211\u4eec\u200b\u4e0e\u200b\u4e4b\u200b\u5171\u4eab\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u7c7b\u522b\u200b\uff1a

  • \u200b\u7f51\u7edc\u200b\u548c\u200b\u79fb\u52a8\u200b\u5206\u6790\u200b
    • Google Analytics
"},{"location":"zh/about/privacy/#5-cookies","title":"5. \u200b\u6211\u4eec\u200b\u662f\u5426\u200b\u4f7f\u7528\u200bcookies\u200b\u548c\u200b\u5176\u4ed6\u200b\u8ddf\u8e2a\u200b\u6280\u672f\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f7f\u7528\u200bcookies\u200b\u548c\u200b\u5176\u4ed6\u200b\u8ddf\u8e2a\u200b\u6280\u672f\u200b\u6765\u200b\u6536\u96c6\u200b\u548c\u200b\u5b58\u50a8\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u8fd8\u200b\u5141\u8bb8\u200b\u7b2c\u4e09\u65b9\u200b\u548c\u200b\u670d\u52a1\u63d0\u4f9b\u5546\u200b\u5728\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u4e2d\u200b\u4f7f\u7528\u200b\u5728\u7ebf\u200b\u8ddf\u8e2a\u200b\u6280\u672f\u200b\u7528\u4e8e\u200b\u5206\u6790\u200b\u548c\u200b\u5e7f\u544a\u200b\uff0c\u200b\u5305\u62ec\u200b\u5e2e\u52a9\u200b\u7ba1\u7406\u200b\u548c\u200b\u5c55\u793a\u200b\u5e7f\u544a\u200b\uff0c\u200b\u6839\u636e\u200b\u60a8\u200b\u7684\u200b\u5174\u8da3\u200b\u5b9a\u5236\u200b\u5e7f\u544a\u200b\uff0c\u200b\u6216\u200b\u53d1\u9001\u200b\u9057\u5f03\u200b\u8d2d\u7269\u8f66\u200b\u63d0\u9192\u200b\uff08\u200b\u53d6\u51b3\u4e8e\u200b\u60a8\u200b\u7684\u200b\u6c9f\u901a\u200b\u504f\u597d\u200b\uff09\u3002 \u200b\u8fd9\u4e9b\u200b\u7b2c\u4e09\u65b9\u200b\u548c\u200b\u670d\u52a1\u63d0\u4f9b\u5546\u200b\u4f7f\u7528\u200b\u4ed6\u4eec\u200b\u7684\u200b\u6280\u672f\u200b\u4e3a\u200b\u60a8\u200b\u63d0\u4f9b\u200b\u5b9a\u5236\u200b\u7684\u200b\u4ea7\u54c1\u200b\u548c\u200b\u670d\u52a1\u200b\u5e7f\u544a\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u5e7f\u544a\u200b\u53ef\u80fd\u200b\u51fa\u73b0\u200b\u5728\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u6216\u200b\u5176\u4ed6\u200b\u7f51\u7ad9\u200b\u4e0a\u200b\u3002

\u200b\u5728\u200b\u9002\u7528\u200b\u7684\u200b\u7f8e\u56fd\u200b\u5dde\u200b\u6cd5\u5f8b\u200b\u4e0b\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u5728\u7ebf\u200b\u8ddf\u8e2a\u200b\u6280\u672f\u200b\u88ab\u200b\u89c6\u4e3a\u200b\u201c\u200b\u9500\u552e\u200b\u201d/\u201c\u200b\u5206\u4eab\u200b\u201d\uff08\u200b\u5305\u62ec\u200b\u76ee\u6807\u200b\u5e7f\u544a\u200b\uff0c\u200b\u6839\u636e\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u5b9a\u4e49\u200b\uff09\u200b\u7684\u200b\u7a0b\u5ea6\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u901a\u8fc7\u200b\u70b9\u51fb\u200b\u672c\u9875\u200b\u9876\u90e8\u200b\u6216\u200b\u4e0b\u9762\u200b\u7684\u200b\u6309\u94ae\u200b\u6765\u200b\u9009\u62e9\u200b\u9000\u51fa\u200b\u8fd9\u4e9b\u200b\u5728\u7ebf\u200b\u8ddf\u8e2a\u200b\u6280\u672f\u200b\uff1a

\u200b\u9690\u79c1\u200b\u63a7\u5236\u200b

"},{"location":"zh/about/privacy/#google-analytics","title":"Google Analytics","text":"

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u4e0e\u200bGoogle Analytics\u200b\u5171\u4eab\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4ee5\u200b\u8ddf\u8e2a\u200b\u548c\u200b\u5206\u6790\u200b\u670d\u52a1\u200b\u7684\u200b\u4f7f\u7528\u200b\u60c5\u51b5\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f7f\u7528\u200b\u7684\u200bGoogle Analytics\u200b\u5e7f\u544a\u200b\u529f\u80fd\u200b\u5305\u62ec\u200b\uff1a

  • Google Analytics\u200b\u7684\u200b\u518d\u200b\u8425\u9500\u200b
  • Google Display Network\u200b\u5370\u8c61\u200b\u62a5\u544a\u200b
  • Google Analytics\u200b\u4eba\u53e3\u7edf\u8ba1\u200b\u548c\u200b\u5174\u8da3\u200b\u62a5\u544a\u200b

\u200b\u8981\u200b\u9009\u62e9\u200b\u9000\u51fa\u200b\u5728\u200b\u670d\u52a1\u200b\u4e2d\u200b\u901a\u8fc7\u200bGoogle Analytics\u200b\u8ddf\u8e2a\u200b\u60a8\u200b\uff0c\u200b\u8bf7\u200b\u8bbf\u95ee\u200bhttps://tools.google.com/dlpage/gaoptout\u3002

\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u901a\u8fc7\u200b\u5e7f\u544a\u200b\u8bbe\u7f6e\u200b\u548c\u200b\u79fb\u52a8\u200b\u5e94\u7528\u200b\u7684\u200b\u5e7f\u544a\u200b\u8bbe\u7f6e\u200b\u6765\u200b\u9009\u62e9\u200b\u9000\u51fa\u200bGoogle Analytics\u200b\u5e7f\u544a\u200b\u529f\u80fd\u200b\u3002

\u200b\u5176\u4ed6\u200b\u9000\u51fa\u200b\u65b9\u5f0f\u200b\u5305\u62ec\u200bhttp://optout.networkadvertising.org/\u200b\u548c\u200bhttp://www.networkadvertising.org/mobile-choice\u3002

\u200b\u6709\u5173\u200bGoogle\u200b\u9690\u79c1\u200b\u505a\u6cd5\u200b\u7684\u200b\u66f4\u200b\u591a\u200b\u4fe1\u606f\u200b\uff0c\u200b\u8bf7\u200b\u8bbf\u95ee\u200bGoogle\u200b\u9690\u79c1\u200b\u4e0e\u200b\u6761\u6b3e\u200b\u3002

"},{"location":"zh/about/privacy/#6","title":"6. \u200b\u6211\u4eec\u200b\u4fdd\u7559\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u591a\u4e45\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u5c06\u200b\u6839\u636e\u200b\u672c\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u4e2d\u200b\u6982\u8ff0\u200b\u7684\u200b\u76ee\u7684\u200b\u4fdd\u7559\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u9664\u975e\u200b\u6cd5\u5f8b\u200b\u53e6\u6709\u200b\u8981\u6c42\u200b\u3002

\u200b\u6211\u4eec\u200b\u53ea\u4f1a\u200b\u5728\u200b\u672c\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u4e2d\u200b\u6982\u8ff0\u200b\u7684\u200b\u76ee\u7684\u200b\u6240\u200b\u9700\u200b\u7684\u200b\u65f6\u95f4\u200b\u5185\u200b\u4fdd\u7559\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u9664\u975e\u200b\u6cd5\u5f8b\u200b\u8981\u6c42\u200b\u6216\u200b\u5141\u8bb8\u200b\u66f4\u957f\u200b\u7684\u200b\u4fdd\u7559\u200b\u671f\u200b\uff08\u200b\u5982\u200b\u7a0e\u52a1\u200b\u3001\u200b\u4f1a\u8ba1\u200b\u6216\u200b\u5176\u4ed6\u200b\u6cd5\u5f8b\u200b\u8981\u6c42\u200b\uff09\u3002

\u200b\u5f53\u200b\u6211\u4eec\u200b\u6ca1\u6709\u200b\u6301\u7eed\u200b\u7684\u200b\u5408\u6cd5\u200b\u4e1a\u52a1\u200b\u9700\u8981\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u65f6\u200b\uff0c\u200b\u6211\u4eec\u200b\u5c06\u200b\u5220\u9664\u200b\u6216\u200b\u533f\u540d\u200b\u5316\u5b83\u200b\uff0c\u200b\u6216\u8005\u200b\uff0c\u200b\u5982\u679c\u200b\u8fd9\u200b\u4e0d\u200b\u53ef\u80fd\u200b\uff08\u200b\u4f8b\u5982\u200b\uff0c\u200b\u56e0\u4e3a\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u5df2\u200b\u5b58\u50a8\u200b\u5728\u200b\u5907\u4efd\u200b\u6863\u6848\u200b\u4e2d\u200b\uff09\uff0c\u200b\u90a3\u4e48\u200b\u6211\u4eec\u200b\u5c06\u200b\u5b89\u5168\u200b\u5730\u200b\u5b58\u50a8\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u5e76\u200b\u5c06\u200b\u5176\u200b\u4e0e\u200b\u4efb\u4f55\u200b\u8fdb\u4e00\u6b65\u200b\u5904\u7406\u200b\u9694\u79bb\u200b\uff0c\u200b\u76f4\u5230\u200b\u5220\u9664\u200b\u6210\u4e3a\u200b\u53ef\u80fd\u200b\u3002

"},{"location":"zh/about/privacy/#7","title":"7. \u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u4fdd\u6301\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u5b89\u5168\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u65e8\u5728\u200b\u901a\u8fc7\u200b\u4e00\u7cfb\u5217\u200b\u7ec4\u7ec7\u200b\u548c\u200b\u6280\u672f\u200b\u5b89\u5168\u63aa\u65bd\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u5df2\u200b\u5b9e\u65bd\u200b\u9002\u5f53\u200b\u7684\u200b\u6280\u672f\u200b\u548c\u200b\u7ec4\u7ec7\u200b\u5b89\u5168\u63aa\u65bd\u200b\uff0c\u200b\u65e8\u5728\u200b\u4fdd\u62a4\u200b\u6211\u4eec\u200b\u5904\u7406\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7684\u200b\u5b89\u5168\u200b\u3002 \u200b\u7136\u800c\u200b\uff0c\u200b\u5c3d\u7ba1\u200b\u6211\u4eec\u200b\u91c7\u53d6\u200b\u4e86\u200b\u4fdd\u969c\u200b\u63aa\u65bd\u200b\u5e76\u200b\u52aa\u529b\u200b\u786e\u4fdd\u60a8\u200b\u7684\u200b\u4fe1\u606f\u5b89\u5168\u200b\uff0c\u200b\u4efb\u4f55\u200b\u901a\u8fc7\u200b\u4e92\u8054\u7f51\u200b\u7684\u200b\u7535\u5b50\u200b\u4f20\u8f93\u200b\u6216\u200b\u4fe1\u606f\u200b\u5b58\u50a8\u6280\u672f\u200b\u90fd\u200b\u65e0\u6cd5\u200b\u4fdd\u8bc1\u200b\u662f\u200b100%\u200b\u5b89\u5168\u200b\u7684\u200b\uff0c\u200b\u56e0\u6b64\u200b\u6211\u4eec\u200b\u65e0\u6cd5\u200b\u627f\u8bfa\u200b\u6216\u200b\u4fdd\u8bc1\u200b\u9ed1\u5ba2\u200b\u3001\u200b\u7f51\u7edc\u200b\u72af\u7f6a\u5206\u5b50\u200b\u6216\u200b\u5176\u4ed6\u200b\u672a\u7ecf\u200b\u6388\u6743\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u4e0d\u4f1a\u200b\u7834\u574f\u200b\u6211\u4eec\u200b\u7684\u200b\u5b89\u5168\u63aa\u65bd\u200b\u5e76\u200b\u4e0d\u200b\u5f53\u5730\u200b\u6536\u96c6\u200b\u3001\u200b\u8bbf\u95ee\u200b\u3001\u200b\u7a83\u53d6\u200b\u6216\u200b\u4fee\u6539\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002 \u200b\u5c3d\u7ba1\u200b\u6211\u4eec\u200b\u5c06\u200b\u5c3d\u200b\u6700\u5927\u200b\u52aa\u529b\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u5230\u200b\u6211\u4eec\u200b\u670d\u52a1\u200b\u7684\u200b\u4f20\u8f93\u200b\u548c\u200b\u4ece\u200b\u6211\u4eec\u200b\u670d\u52a1\u200b\u7684\u200b\u4f20\u8f93\u200b\u4ecd\u7136\u200b\u662f\u200b\u60a8\u200b\u81ea\u5df1\u200b\u7684\u200b\u98ce\u9669\u200b\u3002 \u200b\u60a8\u200b\u5e94\u8be5\u200b\u53ea\u200b\u5728\u200b\u5b89\u5168\u200b\u7684\u200b\u73af\u5883\u200b\u4e2d\u200b\u8bbf\u95ee\u200b\u670d\u52a1\u200b\u3002

"},{"location":"zh/about/privacy/#8","title":"8. \u200b\u60a8\u200b\u6709\u200b\u54ea\u4e9b\u200b\u9690\u79c1\u200b\u6743\u5229\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u52aa\u529b\u200b\u5728\u200b\u6cd5\u5f8b\u200b\u5141\u8bb8\u200b\u7684\u200b\u6700\u5927\u200b\u8303\u56f4\u200b\u5185\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u9690\u79c1\u200b\u6743\u5229\u200b\u548c\u200b\u9009\u62e9\u200b\u3002

\u200b\u60a8\u200b\u5728\u200b\u67d0\u4e9b\u200b\u6570\u636e\u4fdd\u62a4\u200b\u6cd5\u4e0b\u200b\u6709\u200b\u6743\u5229\u200b\u3002 \u200b\u7136\u800c\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u6743\u5229\u200b\u4e0d\u662f\u200b\u7edd\u5bf9\u200b\u7684\u200b\uff0c\u200b\u5728\u200b\u67d0\u4e9b\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u6839\u636e\u200b\u6cd5\u5f8b\u200b\u62d2\u7edd\u200b\u60a8\u200b\u7684\u200b\u8bf7\u6c42\u200b\u3002 \u200b\u8fd9\u4e9b\u200b\u6743\u5229\u200b\u5305\u62ec\u200b\uff1a

  • \u200b\u77e5\u60c5\u6743\u200b \u200b\u6211\u4eec\u200b\u662f\u5426\u200b\u6b63\u5728\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b
  • \u200b\u8bbf\u95ee\u200b\u6743\u200b \u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b
  • \u200b\u66f4\u6b63\u200b\u6743\u200b \u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\u4e2d\u200b\u7684\u200b\u4e0d\u200b\u51c6\u786e\u200b\u4fe1\u606f\u200b
  • \u200b\u8bf7\u6c42\u200b\u5220\u9664\u200b\u6743\u200b \u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b
  • \u200b\u83b7\u53d6\u200b\u526f\u672c\u200b\u6743\u200b \u200b\u60a8\u200b\u4ee5\u524d\u200b\u4e0e\u200b\u6211\u4eec\u200b\u5171\u4eab\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b
  • \u200b\u53cd\u200b\u6b67\u89c6\u200b\u6743\u200b \u200b\u9488\u5bf9\u200b\u60a8\u200b\u884c\u4f7f\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b
  • \u200b\u9009\u62e9\u200b\u9000\u51fa\u200b\u6743\u200b
    • \u200b\u5982\u679c\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\u7528\u4e8e\u200b\u76ee\u6807\u200b\u5e7f\u544a\u200b\uff08\u200b\u6216\u200b\u6839\u636e\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u5b9a\u4e49\u200b\u7684\u200b\u201c\u200b\u5206\u4eab\u200b\u201d\uff09\uff0c\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\u7684\u200b\u9500\u552e\u200b\uff0c\u200b\u6216\u200b\u4fc3\u8fdb\u200b\u5bf9\u200b\u60a8\u200b\u4ea7\u751f\u200b\u6cd5\u5f8b\u200b\u6216\u200b\u7c7b\u4f3c\u200b\u91cd\u5927\u200b\u6548\u679c\u200b\u7684\u200b\u51b3\u7b56\u200b\uff08\u201c\u200b\u5206\u6790\u200b\u201d\uff09\u200b\u7684\u200b\u5206\u6790\u200b
    • \u200b\u6536\u96c6\u200b\u901a\u8fc7\u200b\u8bed\u97f3\u200b\u6216\u200b\u9762\u90e8\u200b\u8bc6\u522b\u200b\u529f\u80fd\u200b\u64cd\u4f5c\u200b\u6536\u96c6\u200b\u7684\u200b\u654f\u611f\u6570\u636e\u200b\u548c\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b
  • \u200b\u83b7\u53d6\u200b\u6743\u200b
    • \u200b\u5411\u200b\u6211\u4eec\u200b\u62ab\u9732\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u7c7b\u522b\u200b\u7684\u200b\u5217\u8868\u200b
    • \u200b\u5411\u200b\u6211\u4eec\u200b\u62ab\u9732\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\u7684\u200b\u7279\u5b9a\u200b\u7b2c\u4e09\u65b9\u200b\u7684\u200b\u5217\u8868\u200b
  • \u200b\u9650\u5236\u200b\u4f7f\u7528\u200b\u548c\u200b\u62ab\u9732\u200b\u6743\u200b \u200b\u654f\u611f\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b
"},{"location":"zh/about/privacy/#_5","title":"\u5982\u4f55\u200b\u884c\u4f7f\u200b\u60a8\u200b\u7684\u200b\u6743\u5229","text":"

\u200b\u60a8\u200b\u51e0\u4e4e\u200b\u4e0d\u200b\u53ef\u80fd\u200b\u884c\u4f7f\u200b\u4e0a\u8ff0\u200b\u6743\u5229\u200b\uff0c\u200b\u56e0\u4e3a\u200b\u6211\u4eec\u200b\u4e0d\u200b\u4ece\u200b\u60a8\u200b\u90a3\u91cc\u200b\u6536\u96c6\u200b\u4efb\u4f55\u200b\u53ef\u200b\u8bc6\u522b\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\u3002

\u200b\u6211\u4eec\u200b\u65e0\u6cd5\u200b\u56de\u590d\u200b\u548c\u200b\u91c7\u53d6\u200b\u6570\u636e\u200b\u4e3b\u4f53\u200b\u8bbf\u95ee\u200b\u8bf7\u6c42\u200b\uff0c\u200b\u56e0\u4e3a\u200b\u6211\u4eec\u200b\u4e0d\u200b\u4fdd\u5b58\u200b\u4efb\u4f55\u200b\u53ef\u200b\u8bc6\u522b\u200b\u7684\u200b\u5173\u4e8e\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u6211\u4eec\u200b\u5c06\u200b\u65e0\u6cd5\u200b\u9a8c\u8bc1\u200b\u60a8\u200b\u7684\u200b\u8eab\u4efd\u200b\u3002

\u200b\u5982\u679c\u200b\u60a8\u200b\u8ba4\u4e3a\u200b\u6211\u4eec\u200b\u975e\u6cd5\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u8054\u7cfb\u200b\u60a8\u200b\u6240\u5728\u200b\u7ba1\u8f96\u533a\u200b\u7684\u200b\u76f8\u5173\u200b\u6570\u636e\u4fdd\u62a4\u200b\u76d1\u7ba1\u200b\u673a\u6784\u200b\u3001\u200b\u5dde\u200b\u603b\u200b\u68c0\u5bdf\u957f\u200b\u6216\u200b\u5176\u4ed6\u200b\u6709\u6743\u200b\u673a\u6784\u200b\u3002

\u200b\u5c45\u4f4f\u5730\u200b \u200b\u673a\u6784\u200b \u200b\u6b27\u6d32\u200b\u7ecf\u6d4e\u533a\u200b \u200b\u6210\u5458\u56fd\u200b\u7684\u200b\u6570\u636e\u4fdd\u62a4\u200b\u76d1\u7763\u673a\u6784\u200b \u200b\u82f1\u56fd\u200b \u200b\u4fe1\u606f\u200b\u4e13\u5458\u200b\u529e\u516c\u5ba4\u200b \u200b\u6fb3\u5927\u5229\u4e9a\u200b \u200b\u6fb3\u5927\u5229\u4e9a\u200b\u4fe1\u606f\u200b\u4e13\u5458\u200b\u529e\u516c\u5ba4\u200b \u200b\u65b0\u897f\u5170\u200b \u200b\u65b0\u897f\u5170\u200b\u9690\u79c1\u200b\u4e13\u5458\u200b\u529e\u516c\u5ba4\u200b \u200b\u52a0\u62ff\u5927\u200b \u200b\u52a0\u62ff\u5927\u200b\u9690\u79c1\u200b\u4e13\u5458\u200b\u529e\u516c\u5ba4\u200b \u200b\u7f8e\u56fd\u200b\u52a0\u5229\u798f\u5c3c\u4e9a\u5dde\u200b \u200b\u52a0\u5229\u798f\u5c3c\u4e9a\u200b\u9690\u79c1\u200b\u4fdd\u62a4\u200b\u673a\u6784\u200b \u200b\u745e\u58eb\u200b \u200b\u8054\u90a6\u200b\u6570\u636e\u4fdd\u62a4\u200b\u548c\u200b\u4fe1\u606f\u200b\u4e13\u5458\u200b \u200b\u5357\u975e\u200b \u200b\u4fe1\u606f\u200b\u76d1\u7ba1\u200b\u673a\u6784"},{"location":"zh/about/privacy/#_6","title":"\u64a4\u56de\u200b\u60a8\u200b\u7684\u200b\u540c\u610f","text":"

\u200b\u5982\u679c\u200b\u6211\u4eec\u200b\u4f9d\u8d56\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u6765\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u8fd9\u200b\u53ef\u80fd\u200b\u662f\u200b\u660e\u786e\u200b\u7684\u200b\u548c\u200b/\u200b\u6216\u200b\u6697\u793a\u200b\u7684\u200b\u540c\u610f\u200b\uff0c\u200b\u53d6\u51b3\u4e8e\u200b\u9002\u7528\u6cd5\u5f8b\u200b\uff0c\u200b\u60a8\u200b\u6709\u6743\u200b\u968f\u65f6\u200b\u64a4\u56de\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u3002 \u200b\u60a8\u200b\u53ef\u4ee5\u200b\u901a\u8fc7\u200b\u70b9\u51fb\u200b\u672c\u9875\u200b\u9876\u90e8\u200b\u6216\u200b\u4e0b\u9762\u200b\u7684\u200b\u6309\u94ae\u200b\u968f\u65f6\u200b\u64a4\u56de\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\uff1a

\u200b\u9690\u79c1\u200b\u63a7\u5236\u200b

\u200b\u7136\u800c\u200b\uff0c\u200b\u8bf7\u200b\u6ce8\u610f\u200b\uff0c\u200b\u8fd9\u200b\u4e0d\u4f1a\u200b\u5f71\u54cd\u200b\u64a4\u56de\u200b\u4e4b\u524d\u200b\u7684\u200b\u5904\u7406\u200b\u7684\u200b\u5408\u6cd5\u6027\u200b\uff0c\u200b\u4e5f\u200b\u4e0d\u4f1a\u200b\u5f71\u54cd\u200b\u5f53\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u5141\u8bb8\u200b\u65f6\u200b\uff0c\u200b\u57fa\u4e8e\u200b\u9664\u200b\u540c\u610f\u200b\u4e4b\u5916\u200b\u7684\u200b\u5408\u6cd5\u200b\u5904\u7406\u200b\u7406\u7531\u200b\u8fdb\u884c\u200b\u7684\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7684\u200b\u5904\u7406\u200b\u3002

"},{"location":"zh/about/privacy/#cookies","title":"Cookies\u200b\u548c\u200b\u7c7b\u4f3c\u200b\u6280\u672f","text":"

\u200b\u5927\u591a\u6570\u200b\u7f51\u7edc\u200b\u6d4f\u89c8\u5668\u200b\u9ed8\u8ba4\u8bbe\u7f6e\u200b\u4e3a\u200b\u63a5\u53d7\u200bcookies\u3002 \u200b\u5982\u679c\u200b\u60a8\u200b\u613f\u610f\u200b\uff0c\u200b\u60a8\u200b\u901a\u5e38\u200b\u53ef\u4ee5\u200b\u9009\u62e9\u200b\u8bbe\u7f6e\u200b\u60a8\u200b\u7684\u200b\u6d4f\u89c8\u5668\u200b\u4ee5\u200b\u5220\u9664\u200b\u6216\u200b\u62d2\u7edd\u200b\u6d4f\u89c8\u5668\u200bcookies\u3002 \u200b\u8bf7\u200b\u6ce8\u610f\u200b\uff0c\u200b\u5982\u679c\u200b\u60a8\u200b\u9009\u62e9\u200b\u5220\u9664\u200b\u6216\u200b\u62d2\u7edd\u200bcookies\uff0c\u200b\u8fd9\u200b\u5c06\u200b\u4e0d\u4f1a\u200b\u5f71\u54cd\u200b\u6211\u4eec\u200b\u670d\u52a1\u200b\u7684\u200b\u53ef\u7528\u6027\u200b\u548c\u200b\u529f\u80fd\u200b\u3002

"},{"location":"zh/about/privacy/#9","title":"9. \u200b\u4e0d\u200b\u8ffd\u8e2a\u200b\u529f\u80fd\u200b\u7684\u200b\u63a7\u5236","text":"

\u200b\u5927\u591a\u6570\u200b\u7f51\u7edc\u200b\u6d4f\u89c8\u5668\u200b\u548c\u200b\u4e00\u4e9b\u200b\u79fb\u52a8\u200b\u64cd\u4f5c\u7cfb\u7edf\u200b\u548c\u200b\u79fb\u52a8\u200b\u5e94\u7528\u7a0b\u5e8f\u200b\u5305\u62ec\u200b\u4e00\u4e2a\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u6fc0\u6d3b\u200b\u7684\u200b\u4e0d\u200b\u8ffd\u8e2a\u200b\uff08\u201cDNT\u201d\uff09\u200b\u529f\u80fd\u200b\u6216\u200b\u8bbe\u7f6e\u200b\uff0c\u200b\u4ee5\u200b\u8868\u8fbe\u200b\u60a8\u200b\u7684\u200b\u9690\u79c1\u200b\u504f\u597d\u200b\uff0c\u200b\u4e0d\u200b\u5e0c\u671b\u200b\u6709\u5173\u200b\u60a8\u200b\u7684\u200b\u5728\u7ebf\u200b\u6d4f\u89c8\u200b\u6d3b\u52a8\u200b\u7684\u200b\u6570\u636e\u200b\u88ab\u200b\u76d1\u63a7\u200b\u548c\u200b\u6536\u96c6\u200b\u3002 \u200b\u5230\u200b\u76ee\u524d\u4e3a\u6b62\u200b\uff0c\u200b\u8fd8\u200b\u6ca1\u6709\u200b\u4e3a\u200b\u8bc6\u522b\u200b\u548c\u200b\u5b9e\u65bd\u200bDNT\u200b\u4fe1\u53f7\u200b\u5236\u5b9a\u200b\u7edf\u4e00\u200b\u7684\u200b\u6280\u672f\u6807\u51c6\u200b\u3002 \u200b\u867d\u7136\u200b\u6211\u4eec\u200b\u4e0d\u80fd\u200b\u627f\u8bfa\u200b\u5c0a\u91cd\u200b\u6bcf\u200b\u4e00\u4e2a\u200bDNT\u200b\u4fe1\u53f7\u200b\uff0c\u200b\u6211\u4eec\u200b\u529b\u6c42\u200b\u5c0a\u91cd\u200b\u6240\u6709\u200b\u5728\u6280\u672f\u4e0a\u200b\u53ef\u884c\u200b\u7684\u200b\u6b64\u7c7b\u200b\u8bf7\u6c42\u200b\u3002

\u200b\u52a0\u5229\u798f\u5c3c\u4e9a\u200b\u6cd5\u5f8b\u200b\u8981\u6c42\u200b\u6211\u4eec\u200b\u544a\u8bc9\u60a8\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u54cd\u5e94\u200b\u7f51\u7edc\u200b\u6d4f\u89c8\u5668\u200b\u7684\u200bDNT\u200b\u4fe1\u53f7\u200b\u3002 \u200b\u7531\u4e8e\u200b\u6211\u4eec\u200b\u4e0d\u80fd\u200b\u4fdd\u8bc1\u200b\u8bc6\u522b\u200b\u548c\u200b\u5c0a\u91cd\u200b\u6240\u6709\u200bDNT\u200b\u4fe1\u53f7\u200b\uff0c\u200b\u6211\u4eec\u200b\u76ee\u524d\u200b\u4e0d\u200b\u5bf9\u200b\u5b83\u4eec\u200b\u505a\u51fa\u200b\u54cd\u5e94\u200b\u3002

"},{"location":"zh/about/privacy/#10","title":"10. \u200b\u67d0\u4e9b\u200b\u7ba1\u8f96\u533a\u200b\u7684\u200b\u5c45\u6c11\u200b\u662f\u5426\u200b\u6709\u200b\u7279\u5b9a\u200b\u7684\u200b\u9690\u79c1\u200b\u6743\u5229\u200b\uff1f","text":"

\u200b\u5426\u200b\u3002

\u200b\u6240\u6709\u200b\u7537\u5973\u200b\u751f\u800c\u5e73\u7b49\u200b\u3002

\u200b\u6211\u4eec\u200b\u5411\u200b\u6240\u6709\u200b\u4e2a\u4eba\u200b\u63d0\u4f9b\u200b\u76f8\u540c\u200b\u7684\u200b\u9690\u79c1\u200b\u6743\u5229\u200b\uff0c\u200b\u65e0\u8bba\u200b\u4ed6\u4eec\u200b\u7684\u200b\u4f4d\u7f6e\u200b\u5982\u4f55\u200b\u3002

\u200b\u8bf7\u200b\u653e\u5fc3\u200b\uff0c\u200b\u6211\u4eec\u200b\u5c06\u200b\u4ee5\u200b\u6211\u4eec\u200b\u5e0c\u671b\u200b\u88ab\u200b\u5bf9\u5f85\u200b\u7684\u200b\u76f8\u540c\u200b\u7684\u200b\u5c0a\u91cd\u200b\u548c\u200b\u5c0a\u4e25\u200b\u5bf9\u5f85\u200b\u60a8\u200b\u3002

"},{"location":"zh/about/privacy/#11","title":"11. \u200b\u60a8\u200b\u5982\u4f55\u200b\u67e5\u770b\u200b\u3001\u200b\u66f4\u65b0\u200b\u6216\u200b\u5220\u9664\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u7684\u200b\u6570\u636e\u200b\uff1f","text":"

\u200b\u60a8\u200b\u51e0\u4e4e\u200b\u4e0d\u200b\u53ef\u80fd\u200b\u67e5\u770b\u200b\u3001\u200b\u66f4\u65b0\u200b\u6216\u200b\u5220\u9664\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u7684\u200b\u6570\u636e\u200b\uff0c\u200b\u56e0\u4e3a\u200b\u6211\u4eec\u200b\u4e0d\u200b\u4ece\u200b\u60a8\u200b\u90a3\u91cc\u200b\u6536\u96c6\u200b\u4efb\u4f55\u200b\u53ef\u200b\u8bc6\u522b\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\uff0c\u200b\u4e5f\u200b\u65e0\u6cd5\u200b\u786e\u5b9a\u200b\u54ea\u4e9b\u200b\u6570\u636e\u200b\u662f\u200b\u5c5e\u4e8e\u200b\u60a8\u200b\u7684\u200b\u3002

"},{"location":"zh/about/privacy/#12","title":"12. \u200b\u6211\u4eec\u200b\u662f\u5426\u200b\u4f1a\u200b\u66f4\u65b0\u200b\u6b64\u200b\u58f0\u660e\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u662f\u200b\u7684\u200b\uff0c\u200b\u6211\u4eec\u200b\u5c06\u200b\u6839\u636e\u200b\u9700\u8981\u200b\u66f4\u65b0\u200b\u6b64\u200b\u58f0\u660e\u200b\u4ee5\u200b\u4fdd\u6301\u200b\u4e0e\u200b\u76f8\u5173\u200b\u6cd5\u5f8b\u200b\u7684\u200b\u4e00\u81f4\u200b\u3002

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u4e0d\u65f6\u200b\u66f4\u65b0\u200b\u6b64\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u3002 \u200b\u66f4\u65b0\u200b\u540e\u200b\u7684\u200b\u7248\u672c\u200b\u5c06\u200b\u901a\u8fc7\u200b\u66f4\u65b0\u200b\u9876\u90e8\u200b\u7684\u200b\u201c\u200b\u6700\u540e\u200b\u4fee\u8ba2\u200b\u65e5\u671f\u200b\u201d\u200b\u6765\u200b\u8868\u793a\u200b\u3002 \u200b\u5982\u679c\u200b\u6211\u4eec\u200b\u8fdb\u884c\u200b\u4efb\u4f55\u200b\u91cd\u5927\u200b\u66f4\u6539\u200b\uff0c\u200b\u6211\u4eec\u200b\u5c06\u200b\u901a\u8fc7\u200b\u5728\u200b\u672c\u9875\u200b\u53d1\u5e03\u200b\u65b0\u200b\u7684\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u6765\u200b\u901a\u77e5\u200b\u60a8\u200b\u3002 \u200b\u7531\u4e8e\u200b\u6211\u4eec\u200b\u4e0d\u200b\u6536\u96c6\u200b\u60a8\u200b\u7684\u200b\u4efb\u4f55\u200b\u8054\u7cfb\u200b\u4fe1\u606f\u200b\uff0c\u200b\u6211\u4eec\u200b\u65e0\u6cd5\u200b\u76f4\u63a5\u200b\u901a\u77e5\u200b\u60a8\u200b\u3002 \u200b\u6211\u4eec\u200b\u9f13\u52b1\u200b\u60a8\u200b\u7ecf\u5e38\u200b\u67e5\u770b\u200b\u672c\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\uff0c\u200b\u4ee5\u200b\u4e86\u89e3\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002

"}]} \ 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 reuse 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 supports variable interpolation. Set a member\u2019s value to another member\u2019s name wrapped in ${}, then call interpolate method. The value of this member will be automatically replaced with the value of another member.

dict in Python is ordered since Python 3.7, but there isn\u2019t a built-in method to help you sort a dict. FlatDictsupports sort to help you manage your dict.

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 work better with DefaultDict.

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 Tensor. 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 determine the type by their extension and dump/load FlatDict to/from a file.

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

To facilitate 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 make 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.

ConfigRegistry is a subclass of Registry that is specifically designed for building objects from a Config or a dataclass. Just specify the key when creating the registry and pass config to the build method, and you will get the object you want.

"},{"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 supports type, choices, validator, and required to ensure the correctness of the value.

To make it even easier, Variable also support 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
# CHANfiG, Easier Configuration.\n# Copyright (c) 2022-Present, CHANfiG Contributors\n\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the following licenses:\n# - The Unlicense\n# - GNU Affero General Public License v3.0 or later\n# - GNU General Public License v2.0 or later\n# - BSD 4-Clause \"Original\" or \"Old\" License\n# - MIT License\n# - Apache License 2.0\n\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n# See the LICENSE file for more details.\n\nimport os\n\nfrom chanfig import Config, Variable, configclass\n\n\n@configclass\nclass DataloaderConfig:\n    batch_size: int = 64\n    num_workers: int = 4\n    pin_memory: bool = True\n\n\nclass TestConfig(Config):\n    def __init__(self):\n        super().__init__()\n        dropout = Variable(0.1)\n        self.name = \"CHANfiG\"\n        self.seed = 1013\n        self.activation = \"GELU\"\n        self.optim.lr = 1e-3\n        self.dataloader = DataloaderConfig()\n        self.model.encoder.num_layers = 6\n        self.model.decoder.num_layers = 6\n        self.model.dropout = dropout\n        self.model.encoder.dropout = dropout\n        self.model.decoder.dropout = dropout\n        self.add_argument(\"--batch_size\", dest=\"data.batch_size\")\n        self.add_argument(\"--lr\", dest=\"optim.lr\")\n\n    def post(self):\n        self.id = f\"{self.name}_{self.seed}\"\n\n\nif __name__ == \"__main__\":\n    # config = Config.load('config.yaml')  # read config from a yaml\n    # config = Config.load('config.json')  # read config from a json\n    existing_config = {\"model.encoder.num_layers\": 8}\n    config = TestConfig()\n    config.merge(existing_config)\n    # config.merge('dataset.yaml')  # merge config from a yaml\n    # config.merge('dataset.json', overwrite=False)  # merge config from a json\n    config = config.parse()\n    config.model.decoder.num_layers = 8\n    config.freeze()\n    print(config)\n    # main(config)\n    dir_path = os.path.dirname(os.path.realpath(__file__))\n    print(dir_path)\n    config.save(os.path.join(dir_path, \"config.yaml\"))  # save config to a yaml\n    config.save(os.path.join(dir_path, \"config.json\"))  # save config to 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:

yamljson YAML
activation: GELU\ndataloader:\n  batch_size: 64\n  num_workers: 4\n  pin_memory: true\nid: CHANfiG_1013\nmodel:\n  decoder:\n    dropout: 0.1\n    num_layers: 8\n  dropout: 0.1\n  encoder:\n    dropout: 0.1\n    num_layers: 8\nname: CHANfiG\noptim:\n  lr: 0.001\nseed: 1013\n
JSON
{\n  \"name\": \"CHANfiG\",\n  \"seed\": 1013,\n  \"activation\": \"GELU\",\n  \"optim\": {\n    \"lr\": 0.001\n  },\n  \"dataloader\": {\n    \"batch_size\": 64,\n    \"num_workers\": 4,\n    \"pin_memory\": true\n  },\n  \"model\": {\n    \"encoder\": {\n      \"num_layers\": 8,\n      \"dropout\": 0.1\n    },\n    \"decoder\": {\n      \"num_layers\": 8,\n      \"dropout\": 0.1\n    },\n    \"dropout\": 0.1\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 pypiInstall the latest version from source Bash
pip install chanfig\n
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:

The UnlicenseGNU Affero General Public License v3.0 or laterGNU General Public License v2.0 or laterBSD 4-Clause \u201cOriginal\u201d or \u201cOld\u201d LicenseMIT LicenseApache License 2.0 Text Only
This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <https://unlicense.org>\n
Text Only
                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<https://www.gnu.org/licenses/>.\n
Text Only
                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n
Text Only
BSD 4-Clause License\n\nCopyright (c) 2022-Present, CHANfiG Contributors\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n3. All advertising materials mentioning features or use of this software must\n   display the following acknowledgement:\n     This product includes software developed by [project].\n\n4. Neither the name of the copyright holder nor the names of its\n   contributors may be used to endorse or promote products derived from\n   this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER \"AS IS\" AND ANY EXPRESS OR\nIMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\nEVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\nOR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\nWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\nOTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\nADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n
Text Only
MIT License\n\nCopyright (c) 2022-Present, CHANfiG Contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n
Text Only
                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n

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):\n    r\"\"\"\n    `Config` is an extension of `NestedDict`.\n\n    The differences between `Config` and `NestedDict` lies in 3 aspects:\n\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\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\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\n    You could also manually call `boot` if you you don't parse command-line arguments.\n\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\n        A **frozen** `Config` does not have this behaviour and\n        will raises `KeyError` when accessing anything that does not exist.\n\n        It is recommended to call `config.freeze()` or `config.to(NestedDict)` to avoid this behaviour.\n\n    Attributes:\n        parser (ConfigParser): Parser for command-line arguments.\n        frozen (bool): If `True`, the config is frozen and cannot be altered.\n\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    \"\"\"\n\n    parser: None  # ConfigParser, Python 3.7 does not support forward reference\n    frozen: bool\n\n    def __init__(self, *args: Any, default_factory: Callable | None = None, **kwargs: Any):\n        if default_factory is None:\n            default_factory = Config\n        self.setattr(\"frozen\", False)\n        super().__init__(*args, default_factory=default_factory, **kwargs)\n\n    def copy_class_attributes(self, recursive: bool = True) -> Self:\n        r\"\"\"\n        Copy class attributes to instance.\n\n        Args:\n            recursive:\n\n        Returns:\n            self:\n\n        Examples:\n            >>> class Ancestor(Config):\n            ...     a = 1\n            >>> class Parent(Ancestor):\n            ...     b = 2\n            >>> class Child(Parent):\n            ...     c = 3\n            >>> c = Child()\n            >>> c\n            Child(<class 'chanfig.config.Config'>, )\n            >>> c.copy_class_attributes(recursive=False)\n            Child(<class 'chanfig.config.Config'>,('c'): 3)\n            >>> c.copy_class_attributes()  # doctest: +SKIP\n            Child(<class 'chanfig.config.Config'>,\n                ('a'): 1,\n                ('b'): 2,\n                ('c'): 3\n            )\n        \"\"\"\n\n        def copy_cls_attributes(cls: type) -> Mapping:\n            return {\n                k: v\n                for k, v in cls.__dict__.items()\n                if k not in self\n                and not k.startswith(\"__\")\n                and (not (isinstance(v, (property, staticmethod, classmethod)) or callable(v)))\n            }\n\n        if recursive:\n            for cls in self.__class__.__mro__:\n                if cls.__module__.startswith(\"chanfig\"):\n                    break\n                self.merge(copy_cls_attributes(cls), overwrite=False)\n        else:\n            self.merge(copy_cls_attributes(self.__class__), overwrite=False)\n        return self\n\n    def post(self) -> Self | None:\n        r\"\"\"\n        Post process of `Config`.\n\n        Some `Config` may need to do some post process after `Config` is initialised.\n        `post` is provided for this lazy-initialisation purpose.\n\n        By default, `post` calls `interpolate` to perform variable interpolation.\n\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\n        See Also:\n            [`boot`][chanfig.Config.boot]\n\n        Returns:\n            self:\n\n        Examples:\n            >>> c = Config()\n            >>> c.dne\n            Config(<class 'chanfig.config.Config'>, )\n            >>> c.post()\n            Config(\n              ('dne'): Config()\n            )\n            >>> c.dne2\n            Traceback (most recent call last):\n            AttributeError: 'Config' object has no attribute 'dne2'\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        \"\"\"\n\n        self.interpolate()\n        self.validate()\n        self.apply_(lambda c: c.setattr(\"default_factory\", None) if isinstance(c, Config) else None)\n        return self\n\n    def boot(self) -> Self:\n        r\"\"\"\n        Apply `post` recursively.\n\n        Sub-config may have their own `post` method.\n        `boot` is provided to apply `post` recursively.\n\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\n        See Also:\n            [`post`][chanfig.Config.post]\n\n        Returns:\n            self:\n\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        \"\"\"\n\n        for value in self.values():\n            if isinstance(value, Config):\n                value.boot()\n        self.post()\n        return self\n\n    def parse(\n        self,\n        args: Iterable[str] | None = None,\n        default_config: str | None = None,\n        no_default_config_action: str = \"raise\",\n        boot: bool = True,\n    ) -> Self:\n        r\"\"\"\n\n        Parse command-line arguments with `ConfigParser`.\n\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\n        By default, this method internally calls `Config.boot()`.\n        To disable this behaviour, set `boot` to `False`.\n\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\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\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        \"\"\"\n\n        if not self.hasattr(\"parser\"):\n            self.setattr(\"parser\", ConfigParser())\n        self.getattr(\"parser\").parse(args, self, default_config, no_default_config_action)\n        if boot:\n            self.boot()\n        return self\n\n    def parse_config(\n        self,\n        args: Iterable[str] | None = None,\n        default_config: str | None = None,\n        no_default_config_action: str = \"raise\",\n        boot: bool = True,\n    ) -> Self:\n        r\"\"\"\n\n        Parse command-line arguments with `ConfigParser`.\n\n        `parse_config` only parse command-line arguments that is in defined in `Config`.\n\n        By default, this method internally calls `Config.boot()`.\n        To disable this behaviour, set `boot` to `False`.\n\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\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\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        \"\"\"\n\n        if not self.hasattr(\"parser\"):\n            self.setattr(\"parser\", ConfigParser())\n        self.getattr(\"parser\").parse_config(args, self, default_config, no_default_config_action)\n        if boot:\n            self.boot()\n        return self\n\n    def add_argument(self, *args: Any, **kwargs: Any) -> None:\n        r\"\"\"\n        Add an argument to `ConfigParser`.\n\n        Note that value defined in `Config` will override the default value defined in `add_argument`.\n\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        \"\"\"\n\n        if not self.hasattr(\"parser\"):\n            self.setattr(\"parser\", ConfigParser())\n        return self.getattr(\"parser\").add_argument(*args, **kwargs)\n\n    def freeze(self, recursive: bool = True) -> Self:\n        r\"\"\"\n        Freeze `Config`.\n\n        Args:\n            recursive:\n\n        **Alias**:\n\n        + `lock`\n\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\n        @wraps(self.freeze)\n        def freeze(config: Config) -> None:\n            if isinstance(config, Config):\n                config.setattr(\"frozen\", True)\n\n        if recursive:\n            self.apply_(freeze)\n        else:\n            freeze(self)\n        return self\n\n    def lock(self, recursive: bool = True) -> Self:\n        r\"\"\"\n        Alias of [`freeze`][chanfig.Config.freeze].\n        \"\"\"\n        return self.freeze(recursive=recursive)\n\n    @contextmanager\n    def locked(self):\n        \"\"\"\n        Context manager which temporarily locks `Config`.\n\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        \"\"\"\n\n        was_frozen = self.getattr(\"frozen\", False)\n        try:\n            self.freeze()\n            yield self\n        finally:\n            if not was_frozen:\n                self.defrost()\n\n    def defrost(self, recursive: bool = True) -> Self:\n        r\"\"\"\n        Defrost `Config`.\n\n        Args:\n            recursive:\n\n        **Alias**:\n\n        + `unlock`\n\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\n        @wraps(self.defrost)\n        def defrost(config: Config) -> None:\n            if isinstance(config, Config):\n                config.setattr(\"frozen\", False)\n\n        if recursive:\n            self.apply_(defrost)\n        else:\n            defrost(self)\n        return self\n\n    def unlock(self, recursive: bool = True) -> Self:\n        r\"\"\"\n        Alias of [`defrost`][chanfig.Config.defrost].\n        \"\"\"\n        return self.defrost(recursive=recursive)\n\n    @contextmanager\n    def unlocked(self):\n        \"\"\"\n        Context manager which temporarily unlocks `Config`.\n\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        \"\"\"\n\n        was_frozen = self.getattr(\"frozen\", False)\n        try:\n            self.defrost()\n            yield self\n        finally:\n            if was_frozen:\n                self.freeze()\n\n    def get(self, name: Any, default: Any = None, fallback: bool | None = None) -> Any:\n        r\"\"\"\n        Get value from `Config`.\n\n        Note that `default` has higher priority than `default_factory`.\n\n        Args:\n            name:\n            default:\n\n        Returns:\n            value:\n                If `Config` does not contain `name`, return `default`.\n                If `default` is not specified, return `default_factory()`.\n\n        Raises:\n            KeyError: If `Config` does not contain `name` and `default`/`default_factory` is not specified.\n\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        \"\"\"\n\n        if not self.hasattr(\"default_factory\"):  # did not call super().__init__() in sub-class\n            self.setattr(\"default_factory\", Config)\n        if name in self or not self.getattr(\"frozen\", False):\n            return super().get(name, default, fallback)\n        raise KeyError(name)\n\n    @frozen_check\n    def set(\n        self,\n        name: Any,\n        value: Any,\n        convert_mapping: bool | None = None,\n    ) -> None:\n        r\"\"\"\n        Set value of `Config`.\n\n        Args:\n            name:\n            value:\n            convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n                Defaults to self.convert_mapping.\n\n        Raises:\n            ValueError: If `Config` is frozen.\n\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        \"\"\"\n\n        return super().set(name, value, convert_mapping)\n\n    @frozen_check\n    def delete(self, name: Any) -> None:\n        r\"\"\"\n        Delete value from `Config`.\n\n        Args:\n            name:\n\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        \"\"\"\n\n        super().delete(name)\n\n    @frozen_check\n    def pop(self, name: Any, default: Any = Null) -> Any:\n        r\"\"\"\n        Pop value from `Config`.\n\n        Args:\n            name:\n            default:\n\n        Returns:\n            value: If `Config` does not contain `name`, return `default`.\n\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        \"\"\"\n\n        return 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:\n    r\"\"\"\n    Add an argument to `ConfigParser`.\n\n    Note that value defined in `Config` will override the default value defined in `add_argument`.\n\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    \"\"\"\n\n    if not self.hasattr(\"parser\"):\n        self.setattr(\"parser\", ConfigParser())\n    return 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 Self

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) -> Self:\n    r\"\"\"\n    Apply `post` recursively.\n\n    Sub-config may have their own `post` method.\n    `boot` is provided to apply `post` recursively.\n\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\n    See Also:\n        [`post`][chanfig.Config.post]\n\n    Returns:\n        self:\n\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    \"\"\"\n\n    for value in self.values():\n        if isinstance(value, Config):\n            value.boot()\n    self.post()\n    return self\n
"},{"location":"config/#chanfig.config.Config.copy_class_attributes","title":"copy_class_attributes(recursive=True)","text":"

Copy class attributes to instance.

Parameters:

Name Type Description Default recursive bool True

Returns:

Name Type Description self Self

Examples:

Python Console Session
>>> class Ancestor(Config):\n...     a = 1\n>>> class Parent(Ancestor):\n...     b = 2\n>>> class Child(Parent):\n...     c = 3\n>>> c = Child()\n>>> c\nChild(<class 'chanfig.config.Config'>, )\n>>> c.copy_class_attributes(recursive=False)\nChild(<class 'chanfig.config.Config'>,('c'): 3)\n>>> c.copy_class_attributes()\nChild(<class 'chanfig.config.Config'>,\n    ('a'): 1,\n    ('b'): 2,\n    ('c'): 3\n)\n
Source code in chanfig/config.py Python
def copy_class_attributes(self, recursive: bool = True) -> Self:\n    r\"\"\"\n    Copy class attributes to instance.\n\n    Args:\n        recursive:\n\n    Returns:\n        self:\n\n    Examples:\n        >>> class Ancestor(Config):\n        ...     a = 1\n        >>> class Parent(Ancestor):\n        ...     b = 2\n        >>> class Child(Parent):\n        ...     c = 3\n        >>> c = Child()\n        >>> c\n        Child(<class 'chanfig.config.Config'>, )\n        >>> c.copy_class_attributes(recursive=False)\n        Child(<class 'chanfig.config.Config'>,('c'): 3)\n        >>> c.copy_class_attributes()  # doctest: +SKIP\n        Child(<class 'chanfig.config.Config'>,\n            ('a'): 1,\n            ('b'): 2,\n            ('c'): 3\n        )\n    \"\"\"\n\n    def copy_cls_attributes(cls: type) -> Mapping:\n        return {\n            k: v\n            for k, v in cls.__dict__.items()\n            if k not in self\n            and not k.startswith(\"__\")\n            and (not (isinstance(v, (property, staticmethod, classmethod)) or callable(v)))\n        }\n\n    if recursive:\n        for cls in self.__class__.__mro__:\n            if cls.__module__.startswith(\"chanfig\"):\n                break\n            self.merge(copy_cls_attributes(cls), overwrite=False)\n    else:\n        self.merge(copy_cls_attributes(self.__class__), overwrite=False)\n    return self\n
"},{"location":"config/#chanfig.config.Config.defrost","title":"defrost(recursive=True)","text":"

Defrost Config.

Parameters:

Name Type Description Default recursive bool True

Alias:

  • unlock

Examples:

Python Console Session
>>> c = Config(**{'i.d': 1013})\n>>> c.getattr('frozen')\nFalse\n>>> c.freeze().dict()\n{'i': {'d': 1013}}\n>>> c.getattr('frozen')\nTrue\n>>> c.defrost(recursive=False).dict()\n{'i': {'d': 1013}}\n>>> c.getattr('frozen')\nFalse\n>>> c.i.getattr('frozen')\nTrue\n>>> c.unlock().dict()  # alias\n{'i': {'d': 1013}}\n>>> c.i.getattr('frozen')\nFalse\n
Source code in chanfig/config.py Python
def defrost(self, recursive: bool = True) -> Self:\n    r\"\"\"\n    Defrost `Config`.\n\n    Args:\n        recursive:\n\n    **Alias**:\n\n    + `unlock`\n\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\n    @wraps(self.defrost)\n    def defrost(config: Config) -> None:\n        if isinstance(config, Config):\n            config.setattr(\"frozen\", False)\n\n    if recursive:\n        self.apply_(defrost)\n    else:\n        defrost(self)\n    return 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:\n    r\"\"\"\n    Delete value from `Config`.\n\n    Args:\n        name:\n\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    \"\"\"\n\n    super().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:

  • lock

Examples:

Python Console Session
>>> c = Config(**{'i.d': 1013})\n>>> c.getattr('frozen')\nFalse\n>>> c.freeze(recursive=False).dict()\n{'i': {'d': 1013}}\n>>> c.getattr('frozen')\nTrue\n>>> c.i.getattr('frozen')\nFalse\n>>> c.lock().dict()  # alias\n{'i': {'d': 1013}}\n>>> c.i.getattr('frozen')\nTrue\n
Source code in chanfig/config.py Python
def freeze(self, recursive: bool = True) -> Self:\n    r\"\"\"\n    Freeze `Config`.\n\n    Args:\n        recursive:\n\n    **Alias**:\n\n    + `lock`\n\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\n    @wraps(self.freeze)\n    def freeze(config: Config) -> None:\n        if isinstance(config, Config):\n            config.setattr(\"frozen\", True)\n\n    if recursive:\n        self.apply_(freeze)\n    else:\n        freeze(self)\n    return 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:\n    r\"\"\"\n    Get value from `Config`.\n\n    Note that `default` has higher priority than `default_factory`.\n\n    Args:\n        name:\n        default:\n\n    Returns:\n        value:\n            If `Config` does not contain `name`, return `default`.\n            If `default` is not specified, return `default_factory()`.\n\n    Raises:\n        KeyError: If `Config` does not contain `name` and `default`/`default_factory` is not specified.\n\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    \"\"\"\n\n    if not self.hasattr(\"default_factory\"):  # did not call super().__init__() in sub-class\n        self.setattr(\"default_factory\", Config)\n    if name in self or not self.getattr(\"frozen\", False):\n        return super().get(name, default, fallback)\n    raise 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) -> Self:\n    r\"\"\"\n    Alias of [`freeze`][chanfig.Config.freeze].\n    \"\"\"\n    return 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\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    \"\"\"\n\n    was_frozen = self.getattr(\"frozen\", False)\n    try:\n        self.freeze()\n        yield self\n    finally:\n        if not was_frozen:\n            self.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(\n    self,\n    args: Iterable[str] | None = None,\n    default_config: str | None = None,\n    no_default_config_action: str = \"raise\",\n    boot: bool = True,\n) -> Self:\n    r\"\"\"\n\n    Parse command-line arguments with `ConfigParser`.\n\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\n    By default, this method internally calls `Config.boot()`.\n    To disable this behaviour, set `boot` to `False`.\n\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\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\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    \"\"\"\n\n    if not self.hasattr(\"parser\"):\n        self.setattr(\"parser\", ConfigParser())\n    self.getattr(\"parser\").parse(args, self, default_config, no_default_config_action)\n    if boot:\n        self.boot()\n    return 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(\n    self,\n    args: Iterable[str] | None = None,\n    default_config: str | None = None,\n    no_default_config_action: str = \"raise\",\n    boot: bool = True,\n) -> Self:\n    r\"\"\"\n\n    Parse command-line arguments with `ConfigParser`.\n\n    `parse_config` only parse command-line arguments that is in defined in `Config`.\n\n    By default, this method internally calls `Config.boot()`.\n    To disable this behaviour, set `boot` to `False`.\n\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\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\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    \"\"\"\n\n    if not self.hasattr(\"parser\"):\n        self.setattr(\"parser\", ConfigParser())\n    self.getattr(\"parser\").parse_config(args, self, default_config, no_default_config_action)\n    if boot:\n        self.boot()\n    return 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:\n    r\"\"\"\n    Pop value from `Config`.\n\n    Args:\n        name:\n        default:\n\n    Returns:\n        value: If `Config` does not contain `name`, return `default`.\n\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    \"\"\"\n\n    return 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 Self | None

Examples:

Python Console Session
>>> c = Config()\n>>> c.dne\nConfig(<class 'chanfig.config.Config'>, )\n>>> c.post()\nConfig(\n  ('dne'): Config()\n)\n>>> c.dne2\nTraceback (most recent call last):\nAttributeError: 'Config' object has no attribute 'dne2'\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()\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) -> Self | None:\n    r\"\"\"\n    Post process of `Config`.\n\n    Some `Config` may need to do some post process after `Config` is initialised.\n    `post` is provided for this lazy-initialisation purpose.\n\n    By default, `post` calls `interpolate` to perform variable interpolation.\n\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\n    See Also:\n        [`boot`][chanfig.Config.boot]\n\n    Returns:\n        self:\n\n    Examples:\n        >>> c = Config()\n        >>> c.dne\n        Config(<class 'chanfig.config.Config'>, )\n        >>> c.post()\n        Config(\n          ('dne'): Config()\n        )\n        >>> c.dne2\n        Traceback (most recent call last):\n        AttributeError: 'Config' object has no attribute 'dne2'\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    \"\"\"\n\n    self.interpolate()\n    self.validate()\n    self.apply_(lambda c: c.setattr(\"default_factory\", None) if isinstance(c, Config) else None)\n    return 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(\n    self,\n    name: Any,\n    value: Any,\n    convert_mapping: bool | None = None,\n) -> None:\n    r\"\"\"\n    Set value of `Config`.\n\n    Args:\n        name:\n        value:\n        convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n            Defaults to self.convert_mapping.\n\n    Raises:\n        ValueError: If `Config` is frozen.\n\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    \"\"\"\n\n    return 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) -> Self:\n    r\"\"\"\n    Alias of [`defrost`][chanfig.Config.defrost].\n    \"\"\"\n    return 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\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    \"\"\"\n\n    was_frozen = self.getattr(\"frozen\", False)\n    try:\n        self.defrost()\n        yield self\n    finally:\n        if was_frozen:\n            self.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):\n    r\"\"\"\n    Decorator check if the object is frozen.\n    \"\"\"\n\n    @wraps(func)\n    def decorator(self, *args: Any, **kwargs: Any):\n        if self.getattr(\"frozen\", False):\n            raise ValueError(\"Attempting to alter a frozen config. Run config.defrost() to defrost first.\")\n        return func(self, *args, **kwargs)\n\n    return decorator\n
"},{"location":"configclass/","title":"configclass","text":""},{"location":"configclass/#chanfig.configclasses.configclass","title":"configclass(cls=None, recursive=False)","text":"

Construct a Config in dataclass style.

This decorator creates a Config instance with the provided class attributes.

See Also

dataclass

Parameters:

Name Type Description Default cls Type[Any]

The class to be enhanced, provided directly if no parentheses are used.

None recursive bool

If True, recursively copy class attributes. Only applicable if used with parentheses.

False

Returns:

Type Description

A modified class with Config functionalities or a decorator with bound parameters.

Examples:

Python Console Session
>>> @configclass\n... class DataloaderConfig:\n...     batch_size: int = 64\n...     num_workers: int = 4\n...     pin_memory: bool = True\n>>> config = DataloaderConfig()\n>>> print(config)\nDataloaderConfig(<class 'chanfig.config.Config'>,\n  ('batch_size'): 64\n  ('num_workers'): 4\n  ('pin_memory'): True\n)\n
Source code in chanfig/configclasses.py Python
def configclass(cls=None, recursive: bool = False):\n    \"\"\"\n    Construct a Config in [`dataclass`][dataclasses.dataclass] style.\n\n    This decorator creates a Config instance with the provided class attributes.\n\n    See Also:\n        [`dataclass`][dataclasses.dataclass]\n\n    Args:\n        cls (Type[Any]): The class to be enhanced, provided directly if no parentheses are used.\n        recursive (bool): If True, recursively copy class attributes. Only applicable if used with parentheses.\n\n    Returns:\n        A modified class with Config functionalities or a decorator with bound parameters.\n\n    Examples:\n        >>> @configclass\n        ... class DataloaderConfig:\n        ...     batch_size: int = 64\n        ...     num_workers: int = 4\n        ...     pin_memory: bool = True\n        >>> config = DataloaderConfig()\n        >>> print(config)\n        DataloaderConfig(<class 'chanfig.config.Config'>,\n          ('batch_size'): 64\n          ('num_workers'): 4\n          ('pin_memory'): True\n        )\n    \"\"\"\n\n    def decorator(cls: Type[Any]):\n        if not issubclass(cls, Config):\n            config_cls = type(cls.__name__, (Config, cls), dict(cls.__dict__))\n            cls = config_cls\n\n        cls_init = cls.__init__\n\n        @wraps(cls_init)\n        def init(self, *args, **kwargs):\n            cls_init(self)\n            self.copy_class_attributes(recursive=recursive)\n            self.merge(*args, **kwargs)\n\n        setattr(cls, \"__init__\", init)  # noqa: B010\n\n        return cls\n\n    if cls is None:\n        return decorator\n    else:\n        return decorator(cls)\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):\n    r\"\"\"\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\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\n    Note that just like `collections.defaultdict`, `default_factory()` is called without any arguments.\n\n    Attributes:\n        default_factory: Default factory for defaultdict behaviour.\n\n    Raises:\n        TypeError: If `default_factory` is not callable.\n\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\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    \"\"\"\n\n    default_factory: Optional[Callable] = None\n\n    def __init__(  # pylint: disable=W1113\n        self, default_factory: Callable | None = None, *args: Any, **kwargs: Any\n    ) -> None:\n        super().__init__(*args, **kwargs)\n        if default_factory is not None:\n            if callable(default_factory):\n                self.setattr(\"default_factory\", default_factory)\n            else:\n                raise TypeError(\n                    f\"`default_factory={default_factory}` must be Callable, but got {type(default_factory)}.\"\n                )\n\n    def __missing__(self, name: Any, default=Null) -> Any:  # pylint: disable=R1710\n        if default is Null:\n            if self.getattr(\"default_factory\", None) in (None, Null) or (name.startswith(\"__\") and name.endswith(\"__\")):\n                raise KeyError(name) from None\n            default = self.getattr(\"default_factory\")()\n        if isinstance(default, FlatDict):\n            default.__dict__.update(self.__dict__)\n        super().set(name, default)\n        return default\n\n    def __repr__(self) -> str:\n        if self.default_factory is None:\n            return super().__repr__()\n        super_repr = super().__repr__()[len(self.__class__.__name__) :]  # noqa: E203\n        if len(super_repr) == 2:\n            return f\"{self.__class__.__name__}({self.default_factory}, )\"\n        return f\"{self.__class__.__name__}({self.default_factory},\" + super_repr[1:]\n\n    def add(self, name: Any):\n        r\"\"\"\n        Add a new default factory to the dictionary.\n\n        Args:\n            name:\n\n        Raises:\n            ValueError: If `default_factory` is None.\n\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        \"\"\"\n        if self.default_factory is None:\n            raise ValueError(\"Cannot add to a DefaultDict with no default_factory\")\n        self.set(name, self.default_factory())  # pylint: disable=E1102\n        return 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):\n    r\"\"\"\n    Add a new default factory to the dictionary.\n\n    Args:\n        name:\n\n    Raises:\n        ValueError: If `default_factory` is None.\n\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    \"\"\"\n    if self.default_factory is None:\n        raise ValueError(\"Cannot add to a DefaultDict with no default_factory\")\n    self.set(name, self.default_factory())  # pylint: disable=E1102\n    return 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):\n    r\"\"\"\n    `FlatDict` with attribute-style access.\n\n    `FlatDict` inherits from built-in `dict`.\n\n    It comes with many easy to use helper methods, such as `merge`, `sort`, `difference`, `intersect`.\n\n    It also has full support for IO operations, such as `json` and `yaml`.\n\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\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\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\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\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\n    Attributes:\n        indent: Indentation level in printing and dumping to json or yaml.\n\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\n        Although it is possible to override other internal methods, it is not recommended to do so.\n\n        `__class__`, `__dict__`, and `getattr` are reserved and cannot be overrode in any manner.\n\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\n    # pylint: disable=R0904\n\n    indent: int = 2\n\n    def __init__(self, *args: Any, **kwargs: Any) -> None:\n        if len(args) == 1:\n            arg = args[0]\n            if isinstance(arg, (PathLike, str, bytes)):\n                arg = self.load(arg)\n            elif isinstance(arg, (Namespace,)):\n                arg = vars(arg)\n            args = (arg,)\n        super().__init__(*args, **kwargs)\n\n    def __post_init__(self, *args, **kwargs) -> None:\n        pass\n\n    def __getattribute__(self, name: Any) -> Any:\n        if (name not in (\"getattr\",) and not (name.startswith(\"__\") and name.endswith(\"__\"))) and name in self:\n            if name in dir(self.__class__):\n                value = super().__getattribute__(name)\n                if isinstance(value, (property, staticmethod, classmethod)) or callable(value):\n                    return value\n            return self.get(name)\n        return super().__getattribute__(name)\n\n    def get(self, name: Any, default: Any = None) -> Any:\n        r\"\"\"\n        Get value from `FlatDict`.\n\n        Args:\n            name:\n            default:\n\n        Returns:\n            value:\n                If `FlatDict` does not contain `name`, return `default`.\n\n        Raises:\n            KeyError: If `FlatDict` does not contain `name` and `default` is not specified.\n            TypeError: If `name` is not hashable.\n\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        \"\"\"\n\n        if name in self:\n            return dict.__getitem__(self, name)\n        if default is not Null:\n            return default\n        return self.__missing__(name)\n\n    def __getitem__(self, name: Any) -> Any:\n        return self.get(name, default=Null)\n\n    def __getattr__(self, name: Any) -> Any:\n        try:\n            return self.get(name, default=Null)\n        except KeyError:\n            raise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\n\n    def set(self, name: Any, value: Any) -> None:\n        r\"\"\"\n        Set value of `FlatDict`.\n\n        Args:\n            name:\n            value:\n\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        \"\"\"\n\n        if name is Null:\n            raise ValueError(\"name must not be null\")\n        if name in self and isinstance(self.get(name), Variable):\n            self.get(name).set(value)\n        else:\n            dict.__setitem__(self, name, value)\n\n    def __setitem__(self, name: Any, value: Any) -> None:\n        self.set(name, value)\n\n    def __setattr__(self, name: Any, value: Any) -> None:\n        self.set(name, value)\n\n    def delete(self, name: Any) -> None:\n        r\"\"\"\n        Delete value from `FlatDict`.\n\n        Args:\n            name:\n\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        \"\"\"\n\n        dict.__delitem__(self, name)\n\n    def __delitem__(self, name: Any) -> None:\n        return self.delete(name)\n\n    def __delattr__(self, name: Any) -> None:\n        try:\n            self.delete(name)\n        except KeyError:\n            raise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\n\n    def __missing__(self, name: Any) -> Any:  # pylint: disable=R1710\n        raise KeyError(name)\n\n    def validate(self) -> None:\n        r\"\"\"\n        Validate `FlatDict`.\n\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\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        \"\"\"\n\n        self._validate(self)\n\n    @staticmethod\n    def _validate(obj) -> None:\n        if isinstance(obj, FlatDict):\n            annotations = get_annotations(obj)\n            for name, value in obj.items():\n                if annotations and name in annotations and not isvalid(value, annotations[name]):\n                    raise TypeError(f\"'{name}' has invalid type. Value {value} is not of type {annotations[name]}.\")\n                if isinstance(value, Variable):\n                    try:\n                        value.validate()\n                    except TypeError as exc:\n                        raise TypeError(f\"'{name}' has invalid type. {exc}\") from None\n                    except ValueError as exc:\n                        raise ValueError(f\"'{name}' has invalid value. {exc}\") from None\n\n    def getattr(self, name: str, default: Any = Null) -> Any:\n        r\"\"\"\n        Get attribute of `FlatDict`.\n\n        Note that it won't retrieve value in `FlatDict`,\n\n        Args:\n            name:\n            default:\n\n        Returns:\n            value: If `FlatDict` does not contain `name`, return `default`.\n\n        Raises:\n            AttributeError: If `FlatDict` does not contain `name` and `default` is not specified.\n\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        \"\"\"\n\n        try:\n            if name in self.__dict__:\n                return self.__dict__[name]\n            for cls in self.__class__.__mro__:\n                if name in cls.__dict__:\n                    return cls.__dict__[name]\n            return super().getattr(name, default)  # type: ignore[misc]\n        except AttributeError:\n            if default is not Null:\n                return default\n            raise AttributeError(f\"'{self.__class__.__name__}' object has no attribute '{name}'\") from None\n\n    def setattr(self, name: str, value: Any) -> None:\n        r\"\"\"\n        Set attribute of `FlatDict`.\n\n        Note that it won't alter values in `FlatDict`.\n\n        Args:\n            name:\n            value:\n\n        Warns:\n            RuntimeWarning: If name already exists in `FlatDict`.\n\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        \"\"\"\n\n        if name in self:\n            warn(\n                f\"{name} already exists in {self.__class__.__name__}.\\n\"\n                f\"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.\",\n                RuntimeWarning,\n            )\n        self.__dict__[name] = value\n\n    def delattr(self, name: str) -> None:\n        r\"\"\"\n        Delete attribute of `FlatDict`.\n\n        Note that it won't delete values in `FlatDict`.\n\n        Args:\n            name:\n\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        \"\"\"\n\n        del self.__dict__[name]\n\n    def hasattr(self, name: str) -> bool:\n        r\"\"\"\n        Determine if an attribute exists in `FlatDict`.\n\n        Args:\n            name:\n\n        Returns:\n            (bool):\n\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        \"\"\"\n\n        try:\n            if name in self.__dict__ or name in self.__class__.__dict__:\n                return True\n            return super().hasattr(name)  # type: ignore[misc]\n        except AttributeError:\n            return False\n\n    def dict(self, flatten: bool = False) -> Mapping | Sequence | Set:\n        r\"\"\"\n        Convert `FlatDict` to other `Mapping`.\n\n        Args:\n            flatten: Whether to flatten [`NestedDict`][chanfig.NestedDict].\n\n        Returns:\n            (Mapping):\n\n        See Also:\n            [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`.\n\n        **Alias**:\n\n        + `to_dict`\n\n        Examples:\n            >>> d = FlatDict(a=1, b=2, c=3)\n            >>> d.dict()\n            {'a': 1, 'b': 2, 'c': 3}\n        \"\"\"\n\n        return to_dict(self, flatten)\n\n    def to_dict(self, flatten: bool = False) -> Mapping | Sequence | Set:\n        r\"\"\"\n        Alias of [`dict`][chanfig.FlatDict.dict].\n        \"\"\"\n\n        return self.dict(flatten)\n\n    @classmethod\n    def from_dict(cls, obj: Mapping | Sequence) -> Any:  # pylint: disable=R0911\n        r\"\"\"\n        Convert `Mapping` or `Sequence` to `FlatDict`.\n\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        \"\"\"\n\n        if obj is None:\n            return cls()\n        if issubclass(cls, FlatDict):\n            cls = cls.empty  # type: ignore[assignment] # pylint: disable=W0642\n        if isinstance(obj, Mapping):\n            return cls(obj)\n        if isinstance(obj, Sequence):\n            try:\n                return cls(obj)\n            except ValueError:\n                return [cls(json) for json in obj]\n        raise TypeError(f\"Expected Mapping or Sequence, but got {type(obj)}.\")\n\n    def sort(self, key: Callable | None = None, reverse: bool = False) -> Self:\n        r\"\"\"\n        Sort `FlatDict`.\n\n        Returns:\n            (FlatDict):\n\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        \"\"\"\n\n        items = sorted(self.items(), key=key, reverse=reverse)\n        self.clear()\n        for k, v in items:  # pylint: disable=C0103\n            self[k] = v\n        return self\n\n    def interpolate(  # pylint: disable=R0912\n        self, use_variable: bool = True, interpolators: MutableMapping | None = None, unsafe_eval: bool = False\n    ) -> Self:\n        r\"\"\"\n        Perform Variable interpolation.\n\n        Variable interpolation allows you to set the value of one key to be the value of another key easily.\n\n        Args:\n            use_variable: Whether to convert values to `Variable` objects.\n            interpolators: Mapping contains values for interpolation. Defaults to `self`.\n            unsafe_eval: Whether to evaluate interpolated values.\n\n        Raises:\n            ValueError: If value is not interpolatable.\n            ValueError: If reference to itself.\n            ValueError: If has circular reference.\n\n        See Also:\n            [Variable][`chanfig.Variable`]: Mutable wrapper of immutable objects.\n\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(unsafe_eval=True).dict()\n            {'a': 1, 'b': 1, 'c': 1.1}\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        \"\"\"\n        # pylint: disable=C0103\n\n        interpolators = interpolators or self\n        placeholders: dict[str, list[str]] = {}\n        for key, value in self.all_items():\n            if isinstance(value, list):\n                for v in value:\n                    self.find_placeholders(key, v, placeholders)\n            elif isinstance(value, Mapping):\n                for v in value.values():\n                    self.find_placeholders(key, v, placeholders)\n            else:\n                self.find_placeholders(key, value, placeholders)\n        circular_references = find_circular_reference(placeholders)\n        if circular_references:\n            raise ValueError(f\"Circular reference found: {'->'.join(circular_references)}.\")\n        if use_variable:\n            placeholder_names = {i for j in placeholders.values() for i in j}\n            for name in list(placeholder_names.difference(placeholders.keys())):\n                if name not in interpolators:\n                    raise ValueError(f\"{name} is not found in {interpolators}.\")\n                if not isinstance(interpolators[name], Variable):\n                    interpolators[name] = Variable(interpolators[name])\n        for key, value in placeholders.items():\n            if isinstance(self[key], list):\n                for index, v in enumerate(self[key]):\n                    self[key][index] = self.substitute(v, interpolators, value)\n            elif isinstance(self[key], Mapping):\n                for k, v in self[key].items():\n                    self[key][k] = self.substitute(v, interpolators, value)\n            else:\n                self[key] = self.substitute(self[key], interpolators, value)\n            if unsafe_eval and isinstance(self[key], str):\n                with suppress(SyntaxError):\n                    self[key] = eval(self[key])  # pylint: disable=W0123\n        return self\n\n    @staticmethod\n    def find_placeholders(key, value, placeholders):\n        placeholder = find_placeholders(value)\n        if placeholder:\n            for index, name in enumerate(placeholder):\n                if name.startswith(\".\"):\n                    placeholder[index] = key.rsplit(\".\", 1)[0] + name\n                if key == name:\n                    raise ValueError(f\"Cannot interpolate {key} to itself.\")\n            placeholders[key] = placeholder\n\n    @staticmethod\n    def substitute(placeholder, interpolators, value):\n        try:\n            if len(value) == 1 and placeholder.startswith(\"${\") and placeholder.endswith(\"}\"):\n                return interpolators[value[0]]\n            return placeholder.replace(\"$\", \"\").format(**interpolators)\n        except KeyError as exc:\n            raise ValueError(f\"{exc} is not found in {interpolators}.\") from None\n\n    def merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> Self:\n        r\"\"\"\n        Merge `other` into `FlatDict`.\n\n        Args:\n            *args: `Mapping` or `Sequence` to be merged.\n            overwrite: Whether to overwrite existing values.\n            **kwargs: `Mapping` to be merged.\n\n        Returns:\n            self:\n\n        **Alias**:\n\n        + `union`\n\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        \"\"\"\n\n        if len(args) == 1:\n            args = args[0]\n            if isinstance(args, (PathLike, str, bytes)):\n                args = self.load(args)  # type: ignore[assignment]\n                warn(\n                    \"merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.\",\n                    PendingDeprecationWarning,\n                )\n            self._merge(self, args, overwrite=overwrite)\n        elif len(args) > 1:\n            self._merge(self, args, overwrite=overwrite)\n        if kwargs:\n            self._merge(self, kwargs, overwrite=overwrite)\n        return self\n\n    @staticmethod\n    def _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping:\n        if not that:\n            return this\n        if isinstance(that, Mapping):\n            that = that.items()\n        for key, value in that:\n            if key in this and isinstance(this[key], Mapping):\n                if isinstance(value, Mapping):\n                    FlatDict._merge(this[key], value)\n                elif overwrite:\n                    if isinstance(value, FlatDict):\n                        this.set(key, value)\n                    else:\n                        this[key] = value\n            elif overwrite or key not in this:\n                this.set(key, value)\n        return this\n\n    def union(self, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Alias of [`merge`][chanfig.FlatDict.merge].\n        \"\"\"\n        return self.merge(*args, **kwargs)\n\n    def merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Merge content of `file` into `FlatDict`.\n\n        Args:\n            file (File):\n            *args: Passed to [`load`][chanfig.FlatDict.load].\n            **kwargs: Passed to [`load`][chanfig.FlatDict.load].\n\n        Returns:\n            self:\n\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        \"\"\"\n\n        return self.merge(self.load(file, *args, **kwargs))\n\n    def intersect(self, other: Mapping | Iterable | PathStr) -> Self:\n        r\"\"\"\n        Intersection of `FlatDict` and `other`.\n\n        Args:\n            other (Mapping | Iterable | PathStr):\n\n        Returns:\n            (FlatDict):\n\n        **Alias**:\n\n        + `inter`\n\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        \"\"\"\n\n        if isinstance(other, (PathLike, str, bytes)):\n            other = self.load(other)\n        if isinstance(other, (Mapping,)):\n            other = self.empty(other).items()\n        if not isinstance(other, Iterable):\n            raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n        return self.empty(**{key: value for key, value in other if key in self and self[key] == value})  # type: ignore\n\n    def inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Alias of [`intersect`][chanfig.FlatDict.intersect].\n        \"\"\"\n        return self.intersect(other, *args, **kwargs)\n\n    def difference(self, other: Mapping | Iterable | PathStr) -> Self:\n        r\"\"\"\n        Difference between `FlatDict` and `other`.\n\n        Args:\n            other:\n\n        Returns:\n            (FlatDict):\n\n        **Alias**:\n\n        + `diff`\n\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        \"\"\"\n\n        if isinstance(other, (PathLike, str, bytes)):\n            other = self.load(other)\n        if isinstance(other, (Mapping,)):\n            other = self.empty(other).items()\n        if not isinstance(other, Iterable):\n            raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n        return self.empty(\n            **{key: value for key, value in other if key not in self or self[key] != value}  # type: ignore[misc]\n        )\n\n    def diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Alias of [`difference`][chanfig.FlatDict.difference].\n        \"\"\"\n        return self.difference(other, *args, **kwargs)\n\n    def to(self, cls: str | TorchDevice | TorchDType) -> Self:  # pragma: no cover\n        r\"\"\"\n        Convert values of `FlatDict` to target `cls`.\n\n        Args:\n            cls (str | torch.device | torch.dtype):\n\n        Returns:\n            self:\n\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\n        # pylint: disable=C0103\n\n        if isinstance(cls, (str, TorchDevice, TorchDType)):\n            for k, v in self.all_items():\n                if hasattr(v, \"to\"):\n                    self[k] = v.to(cls)\n            return self\n\n        raise TypeError(f\"to() only support torch.dtype and torch.device, but got {cls}.\")\n\n    def cpu(self) -> Self:  # pragma: no cover\n        r\"\"\"\n        Move all tensors to cpu.\n\n        Returns:\n            self:\n\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        \"\"\"\n\n        return self.to(TorchDevice(\"cpu\"))\n\n    def gpu(self) -> Self:  # pragma: no cover\n        r\"\"\"\n        Move all tensors to gpu.\n\n        Returns:\n            self:\n\n        **Alias**:\n\n        + `cuda`\n\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        \"\"\"\n\n        return self.to(TorchDevice(\"cuda\"))\n\n    def cuda(self) -> Self:  # pragma: no cover\n        r\"\"\"\n        Alias of [`gpu`][chanfig.FlatDict.gpu].\n        \"\"\"\n        return self.gpu()\n\n    def tpu(self) -> Self:  # pragma: no cover\n        r\"\"\"\n        Move all tensors to tpu.\n\n        Returns:\n            self:\n\n        **Alias**:\n\n        + `xla`\n\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        \"\"\"\n\n        return self.to(TorchDevice(\"xla\"))\n\n    def xla(self) -> Self:  # pragma: no cover\n        r\"\"\"\n        Alias of [`tpu`][chanfig.FlatDict.tpu].\n        \"\"\"\n        return self.tpu()\n\n    def copy(self) -> Self:\n        r\"\"\"\n        Create a shallow copy of `FlatDict`.\n\n        Returns:\n            (FlatDict):\n\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        \"\"\"\n\n        return copy(self)\n\n    def __deepcopy__(self, memo: Mapping | None = None) -> Self:\n        # pylint: disable=C0103\n\n        if memo is not None and id(self) in memo:\n            return memo[id(self)]\n        ret = self.empty()\n        ret.__dict__.update(deepcopy(self.__dict__))\n        for k, v in self.items():\n            if isinstance(v, FlatDict):\n                ret[k] = v.deepcopy(memo=memo)\n            else:\n                ret[k] = deepcopy(v)\n        return ret\n\n    def deepcopy(self, memo: Mapping | None = None) -> Self:  # pylint: disable=W0613\n        r\"\"\"\n        Create a deep copy of `FlatDict`.\n\n        Returns:\n            (FlatDict):\n\n        **Alias**:\n\n        + `clone`\n\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        \"\"\"\n\n        return deepcopy(self)\n\n    def clone(self, memo: Mapping | None = None) -> Self:\n        r\"\"\"\n        Alias of [`deepcopy`][chanfig.FlatDict.deepcopy].\n        \"\"\"\n        return self.deepcopy(memo=memo)\n\n    def save(  # pylint: disable=W1113\n        self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n    ) -> None:\n        r\"\"\"\n        Save `FlatDict` to file.\n\n        Raises:\n            ValueError: If save to `IO` and `method` is not specified.\n            TypeError: If save to unsupported extension.\n\n        **Alias**:\n\n        + `save`\n\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        \"\"\"\n\n        if method is None:\n            if isinstance(file, (IOBase, IO)):\n                raise ValueError(\"`method` must be specified when saving to IO.\")\n            method = splitext(file)[-1][1:]\n        extension = method.lower()\n        if extension in YAML:\n            return self.yaml(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026\n        if extension in JSON:\n            return self.json(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026\n        raise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\n\n    def dump(  # pylint: disable=W1113\n        self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n    ) -> None:\n        r\"\"\"\n        Alias of [`save`][chanfig.FlatDict.save].\n        \"\"\"\n        return self.save(file, method, *args, **kwargs)\n\n    @classmethod\n    def load(  # pylint: disable=W1113\n        cls, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n    ) -> Self:\n        \"\"\"\n        Load `FlatDict` from file.\n\n        Args:\n            file: File to load from.\n            method: File type, should be in `JSON` or `YAML`.\n\n        Returns:\n            (FlatDict):\n\n        Raises:\n            ValueError: If load from `IO` and `method` is not specified.\n            TypeError: If dump to unsupported extension.\n\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        \"\"\"\n\n        if method is None:\n            if isinstance(file, (IOBase, IO)):\n                raise ValueError(\"`method` must be specified when loading from IO.\")\n            method = splitext(file)[-1][1:]\n        extension = method.lower()\n        if extension in JSON:\n            return cls.from_json(file, *args, **kwargs)\n        if extension in YAML:\n            return cls.from_yaml(file, *args, **kwargs)\n        raise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\n\n    def json(self, file: File, *args: Any, **kwargs: Any) -> None:\n        r\"\"\"\n        Dump `FlatDict` to json file.\n\n        This method internally calls `self.jsons()` to generate json string.\n        You may overwrite `jsons` in case something is not json serializable.\n\n        Examples:\n            >>> d = FlatDict(a=1, b=2, c=3)\n            >>> d.json(\"tests/test.json\")\n        \"\"\"\n\n        with self.open(file, mode=\"w\") as fp:  # pylint: disable=C0103\n            fp.write(self.jsons(*args, **kwargs))\n\n    @classmethod\n    def from_json(cls, file: File, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Construct `FlatDict` from json file.\n\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\n        Returns:\n            (FlatDict):\n\n        Examples:\n            >>> d = FlatDict.from_json('tests/test.json')\n            >>> d.dict()\n            {'a': 1, 'b': 2, 'c': 3}\n        \"\"\"\n\n        with cls.open(file) as fp:  # pylint: disable=C0103\n            if isinstance(file, (IOBase, IO)):\n                return cls.from_jsons(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]\n            return cls.from_jsons(fp.read(), *args, **kwargs)\n\n    def jsons(self, *args: Any, **kwargs: Any) -> str:\n        r\"\"\"\n        Dump `FlatDict` to json string.\n\n        Returns:\n            (str):\n\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        \"\"\"\n\n        kwargs.setdefault(\"cls\", JsonEncoder)\n        kwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\n        return json_dumps(self.dict(), *args, **kwargs)\n\n    @classmethod\n    def from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Construct `FlatDict` from json string.\n\n        Returns:\n            (FlatDict):\n\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        \"\"\"\n\n        return cls.from_dict(json_loads(string, *args, **kwargs))\n\n    def yaml(self, file: File, *args: Any, **kwargs: Any) -> None:\n        r\"\"\"\n        Dump `FlatDict` to yaml file.\n\n        This method internally calls `self.yamls()` to generate yaml string.\n        You may overwrite `yamls` in case something is not yaml serializable.\n\n        Examples:\n            >>> d = FlatDict(a=1, b=2, c=3)\n            >>> d.yaml(\"tests/test.yaml\")\n        \"\"\"\n\n        with self.open(file, mode=\"w\") as fp:  # pylint: disable=C0103\n            self.yamls(fp, *args, **kwargs)\n\n    @classmethod\n    def from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Construct `FlatDict` from yaml file.\n\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\n        Returns:\n            (FlatDict):\n\n        Examples:\n            >>> FlatDict.from_yaml('tests/test.yaml').dict()\n            {'a': 1, 'b': 2, 'c': 3}\n        \"\"\"\n\n        kwargs.setdefault(\"Loader\", YamlLoader)\n        with cls.open(file) as fp:  # pylint: disable=C0103\n            if isinstance(file, (IOBase, IO)):\n                return cls.from_yamls(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]\n            return cls.from_dict(yaml_load(fp, *args, **kwargs))\n\n    def yamls(self, *args: Any, **kwargs: Any) -> str:\n        r\"\"\"\n        Dump `FlatDict` to yaml string.\n\n        Returns:\n            (str):\n\n        Examples:\n            >>> FlatDict(a=1, b=2, c=3).yamls()\n            'a: 1\\nb: 2\\nc: 3\\n'\n        \"\"\"\n\n        kwargs.setdefault(\"Dumper\", YamlDumper)\n        kwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\n        return yaml_dump(self.dict(), *args, **kwargs)\n\n    @classmethod\n    def from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Construct `FlatDict` from yaml string.\n\n        Returns:\n            (FlatDict):\n\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        \"\"\"\n\n        kwargs.setdefault(\"Loader\", SafeLoader)\n        return cls.from_dict(yaml_load(string, *args, **kwargs))\n\n    @staticmethod\n    @contextmanager\n    def open(file: File, *args: Any, encoding: str = \"utf-8\", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]:\n        r\"\"\"\n        Open file IO from file path or IO.\n\n        This methods extends the ability of built-in `open` by allowing it to accept an `IOBase` object.\n\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\n        Yields:\n            (Generator[IOBase | IO, Any, Any]):\n\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        \"\"\"\n\n        if isinstance(file, (IOBase, IO)):\n            yield file\n        elif isinstance(file, (PathLike, str, bytes)):\n            try:\n                file = open(file, *args, encoding=encoding, **kwargs)  # type: ignore[call-overload] # noqa: SIM115\n                yield file  # type: ignore[misc]\n            finally:\n                with suppress(Exception):\n                    file.close()  # type: ignore[union-attr]\n        else:\n            raise TypeError(f\"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}\")\n\n    @classmethod\n    def empty(cls, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Initialise an empty `FlatDict`.\n\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\n        This method will preserve everything in `FlatDict.__class__.__dict__`.\n\n        Returns:\n            (FlatDict):\n\n        See Also:\n            [`empty_like`][chanfig.FlatDict.empty_like]\n\n        Examples:\n            >>> d = FlatDict(a=[])\n            >>> c = d.empty()\n            >>> c.dict()\n            {}\n        \"\"\"\n\n        empty = cls.__new__(cls)\n        empty.merge(*args, **kwargs)  # pylint: disable=W0212\n        return empty\n\n    def empty_like(self, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Initialise an empty copy of `FlatDict`.\n\n        This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`.\n\n        For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this\n        method.\n\n        Returns:\n            (FlatDict):\n\n        See Also:\n            [`empty`][chanfig.FlatDict.empty]\n\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        \"\"\"\n\n        empty = self.empty(*args, **kwargs)\n        empty.__dict__.update(self.__dict__)\n        return empty\n\n    def all_keys(self) -> Generator:\n        r\"\"\"\n        Equivalent to `keys`.\n\n        This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n\n        See Also:\n            [`all_keys`][chanfig.NestedDict.all_keys]\n        \"\"\"\n        yield from self.keys()\n\n    def all_values(self) -> Generator:\n        r\"\"\"\n        Equivalent to `keys`.\n\n        This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n\n        See Also:\n            [`all_values`][chanfig.NestedDict.all_values]\n        \"\"\"\n        yield from self.values()\n\n    def all_items(self) -> Generator:\n        r\"\"\"\n        Equivalent to `keys`.\n\n        This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n\n        See Also:\n            [`all_items`][chanfig.NestedDict.all_items]\n        \"\"\"\n        yield from self.items()\n\n    def dropnull(self) -> Self:\n        r\"\"\"\n        Drop key-value pairs with `Null` value.\n\n        Returns:\n            (FlatDict):\n\n        **Alias**:\n\n        + `dropna`\n\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        \"\"\"\n\n        return self.empty({k: v for k, v in self.all_items() if v is not Null})\n\n    def dropna(self) -> Self:\n        r\"\"\"\n        Alias of [`dropnull`][chanfig.FlatDict.dropnull].\n        \"\"\"\n        return self.dropnull()\n\n    @staticmethod\n    def extra_repr() -> str:  # pylint: disable=C0116\n        return \"\"\n\n    def __repr__(self) -> str:\n        extra_lines = []\n        extra_repr = self.extra_repr()\n        # empty string will be split into list ['']\n        if extra_repr:\n            extra_lines = extra_repr.split(\"\\n\")\n        child_lines = []\n        for key, value in self.items():\n            key_repr = repr(key)\n            value_repr = repr(value)\n            value_repr = self._add_indent(value_repr)\n            child_lines.append(f\"({key_repr}): {value_repr}\")\n            # child_lines.append(f\"{key_repr}: {value_repr}\")\n        lines = extra_lines + child_lines\n\n        main_repr = self.__class__.__name__ + \"(\"\n        if lines:\n            # simple one-liner info, which most builtin Modules will use\n            if len(extra_lines) == 1 and not child_lines:\n                main_repr += extra_lines[0]\n            elif len(child_lines) == 1 and not extra_lines and len(child_lines[0]) < 10:\n                main_repr += child_lines[0]\n            else:\n                main_repr += \"\\n  \" + \"\\n  \".join(lines) + \"\\n\"\n\n        main_repr += \")\"\n        return main_repr\n\n    def _add_indent(self, text: str) -> str:\n        lines = text.split(\"\\n\")\n        # don't do anything for single-line stuff\n        if len(lines) == 1:\n            return text\n        first = lines.pop(0)\n        lines = [(self.getattr(\"indent\", 2) * \" \") + line for line in lines]\n        text = \"\\n\".join(lines)\n        text = first + \"\\n\" + text\n        return text\n\n    def __format__(self, format_spec: str) -> str:\n        return repr(self.empty({k: v.__format__(format_spec) for k, v in self.all_items()}))\n\n    def __hash__(self):\n        return hash(frozenset(self.items()))\n\n    def _ipython_display_(self):  # pragma: no cover\n        return repr(self)\n\n    def _ipython_canary_method_should_not_exist_(self):  # pragma: no cover\n        return None\n\n    def aihwerij235234ljsdnp34ksodfipwoe234234jlskjdf(self):  # pragma: no cover\n        return None\n\n    def __rich__(self):  # pragma: no cover\n        return 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:\n    r\"\"\"\n    Equivalent to `keys`.\n\n    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n\n    See Also:\n        [`all_items`][chanfig.NestedDict.all_items]\n    \"\"\"\n    yield 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:\n    r\"\"\"\n    Equivalent to `keys`.\n\n    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n\n    See Also:\n        [`all_keys`][chanfig.NestedDict.all_keys]\n    \"\"\"\n    yield 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:\n    r\"\"\"\n    Equivalent to `keys`.\n\n    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.\n\n    See Also:\n        [`all_values`][chanfig.NestedDict.all_values]\n    \"\"\"\n    yield 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) -> Self:\n    r\"\"\"\n    Alias of [`deepcopy`][chanfig.FlatDict.deepcopy].\n    \"\"\"\n    return 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) -> Self:\n    r\"\"\"\n    Create a shallow copy of `FlatDict`.\n\n    Returns:\n        (FlatDict):\n\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    \"\"\"\n\n    return copy(self)\n
"},{"location":"flat_dict/#chanfig.FlatDict.cpu","title":"cpu()","text":"

Move all tensors to cpu.

Returns:

Name Type Description self Self

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) -> Self:  # pragma: no cover\n    r\"\"\"\n    Move all tensors to cpu.\n\n    Returns:\n        self:\n\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    \"\"\"\n\n    return 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) -> Self:  # pragma: no cover\n    r\"\"\"\n    Alias of [`gpu`][chanfig.FlatDict.gpu].\n    \"\"\"\n    return 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:

  • clone

Examples:

Python Console Session
>>> d = FlatDict(a=[])\n>>> d.setattr(\"name\", \"Chang\")\n>>> c = d.deepcopy()\n>>> c.dict()\n{'a': []}\n>>> d.a.append(1)\n>>> c.dict()\n{'a': []}\n>>> c.getattr(\"name\")\n'Chang'\n>>> d == d.clone()  # alias\nTrue\n
Source code in chanfig/flat_dict.py Python
def deepcopy(self, memo: Mapping | None = None) -> Self:  # pylint: disable=W0613\n    r\"\"\"\n    Create a deep copy of `FlatDict`.\n\n    Returns:\n        (FlatDict):\n\n    **Alias**:\n\n    + `clone`\n\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    \"\"\"\n\n    return 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:\n    r\"\"\"\n    Delete attribute of `FlatDict`.\n\n    Note that it won't delete values in `FlatDict`.\n\n    Args:\n        name:\n\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    \"\"\"\n\n    del 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:\n    r\"\"\"\n    Delete value from `FlatDict`.\n\n    Args:\n        name:\n\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    \"\"\"\n\n    dict.__delitem__(self, name)\n
"},{"location":"flat_dict/#chanfig.FlatDict.dict","title":"dict(flatten=False)","text":"

Convert FlatDict to other Mapping.

Parameters:

Name Type Description Default flatten bool

Whether to flatten NestedDict.

False

Returns:

Type Description Mapping See Also

to_dict: Implementation of dict.

Alias:

  • to_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, flatten: bool = False) -> Mapping | Sequence | Set:\n    r\"\"\"\n    Convert `FlatDict` to other `Mapping`.\n\n    Args:\n        flatten: Whether to flatten [`NestedDict`][chanfig.NestedDict].\n\n    Returns:\n        (Mapping):\n\n    See Also:\n        [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`.\n\n    **Alias**:\n\n    + `to_dict`\n\n    Examples:\n        >>> d = FlatDict(a=1, b=2, c=3)\n        >>> d.dict()\n        {'a': 1, 'b': 2, 'c': 3}\n    \"\"\"\n\n    return to_dict(self, flatten)\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) -> Self:\n    r\"\"\"\n    Alias of [`difference`][chanfig.FlatDict.difference].\n    \"\"\"\n    return 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:

  • diff

Examples:

Python Console Session
>>> d = FlatDict(a=1, b=2, c=3)\n>>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d.difference(n).dict()\n{'b': 'b', 'c': 'c', 'd': 'd'}\n>>> l = [('c', 3), ('d', 4)]\n>>> d.difference(l).dict()\n{'d': 4}\n>>> d.merge(l).difference(\"tests/test.yaml\").dict()\n{}\n>>> d.difference(1)\nTraceback (most recent call last):\nTypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n>>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict()  # alias\n{'b': 'b', 'c': 'c', 'd': 'd'}\n
Source code in chanfig/flat_dict.py Python
def difference(self, other: Mapping | Iterable | PathStr) -> Self:\n    r\"\"\"\n    Difference between `FlatDict` and `other`.\n\n    Args:\n        other:\n\n    Returns:\n        (FlatDict):\n\n    **Alias**:\n\n    + `diff`\n\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    \"\"\"\n\n    if isinstance(other, (PathLike, str, bytes)):\n        other = self.load(other)\n    if isinstance(other, (Mapping,)):\n        other = self.empty(other).items()\n    if not isinstance(other, Iterable):\n        raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n    return self.empty(\n        **{key: value for key, value in other if key not in self or self[key] != value}  # type: ignore[misc]\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) -> Self:\n    r\"\"\"\n    Alias of [`dropnull`][chanfig.FlatDict.dropnull].\n    \"\"\"\n    return self.dropnull()\n
"},{"location":"flat_dict/#chanfig.FlatDict.dropnull","title":"dropnull()","text":"

Drop key-value pairs with Null value.

Returns:

Type Description FlatDict

Alias:

  • dropna

Examples:

Python Console Session
>>> d = FlatDict(a=Null, b=Null, c=3)\n>>> d.dict()\n{'a': Null, 'b': Null, 'c': 3}\n>>> d.dropnull().dict()\n{'c': 3}\n>>> d.dropna().dict()  # alias\n{'c': 3}\n
Source code in chanfig/flat_dict.py Python
def dropnull(self) -> Self:\n    r\"\"\"\n    Drop key-value pairs with `Null` value.\n\n    Returns:\n        (FlatDict):\n\n    **Alias**:\n\n    + `dropna`\n\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    \"\"\"\n\n    return 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(  # pylint: disable=W1113\n    self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n) -> None:\n    r\"\"\"\n    Alias of [`save`][chanfig.FlatDict.save].\n    \"\"\"\n    return 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) -> Self:\n    r\"\"\"\n    Initialise an empty `FlatDict`.\n\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\n    This method will preserve everything in `FlatDict.__class__.__dict__`.\n\n    Returns:\n        (FlatDict):\n\n    See Also:\n        [`empty_like`][chanfig.FlatDict.empty_like]\n\n    Examples:\n        >>> d = FlatDict(a=[])\n        >>> c = d.empty()\n        >>> c.dict()\n        {}\n    \"\"\"\n\n    empty = cls.__new__(cls)\n    empty.merge(*args, **kwargs)  # pylint: disable=W0212\n    return 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) -> Self:\n    r\"\"\"\n    Initialise an empty copy of `FlatDict`.\n\n    This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`.\n\n    For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this\n    method.\n\n    Returns:\n        (FlatDict):\n\n    See Also:\n        [`empty`][chanfig.FlatDict.empty]\n\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    \"\"\"\n\n    empty = self.empty(*args, **kwargs)\n    empty.__dict__.update(self.__dict__)\n    return 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\n    r\"\"\"\n    Convert `Mapping` or `Sequence` to `FlatDict`.\n\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    \"\"\"\n\n    if obj is None:\n        return cls()\n    if issubclass(cls, FlatDict):\n        cls = cls.empty  # type: ignore[assignment] # pylint: disable=W0642\n    if isinstance(obj, Mapping):\n        return cls(obj)\n    if isinstance(obj, Sequence):\n        try:\n            return cls(obj)\n        except ValueError:\n            return [cls(json) for json in obj]\n    raise 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) -> Self:\n    r\"\"\"\n    Construct `FlatDict` from json file.\n\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\n    Returns:\n        (FlatDict):\n\n    Examples:\n        >>> d = FlatDict.from_json('tests/test.json')\n        >>> d.dict()\n        {'a': 1, 'b': 2, 'c': 3}\n    \"\"\"\n\n    with cls.open(file) as fp:  # pylint: disable=C0103\n        if isinstance(file, (IOBase, IO)):\n            return cls.from_jsons(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]\n        return 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) -> Self:\n    r\"\"\"\n    Construct `FlatDict` from json string.\n\n    Returns:\n        (FlatDict):\n\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    \"\"\"\n\n    return 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) -> Self:\n    r\"\"\"\n    Construct `FlatDict` from yaml file.\n\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\n    Returns:\n        (FlatDict):\n\n    Examples:\n        >>> FlatDict.from_yaml('tests/test.yaml').dict()\n        {'a': 1, 'b': 2, 'c': 3}\n    \"\"\"\n\n    kwargs.setdefault(\"Loader\", YamlLoader)\n    with cls.open(file) as fp:  # pylint: disable=C0103\n        if isinstance(file, (IOBase, IO)):\n            return cls.from_yamls(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]\n        return 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) -> Self:\n    r\"\"\"\n    Construct `FlatDict` from yaml string.\n\n    Returns:\n        (FlatDict):\n\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    \"\"\"\n\n    kwargs.setdefault(\"Loader\", SafeLoader)\n    return 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:\n    r\"\"\"\n    Get value from `FlatDict`.\n\n    Args:\n        name:\n        default:\n\n    Returns:\n        value:\n            If `FlatDict` does not contain `name`, return `default`.\n\n    Raises:\n        KeyError: If `FlatDict` does not contain `name` and `default` is not specified.\n        TypeError: If `name` is not hashable.\n\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    \"\"\"\n\n    if name in self:\n        return dict.__getitem__(self, name)\n    if default is not Null:\n        return default\n    return 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:\n    r\"\"\"\n    Get attribute of `FlatDict`.\n\n    Note that it won't retrieve value in `FlatDict`,\n\n    Args:\n        name:\n        default:\n\n    Returns:\n        value: If `FlatDict` does not contain `name`, return `default`.\n\n    Raises:\n        AttributeError: If `FlatDict` does not contain `name` and `default` is not specified.\n\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    \"\"\"\n\n    try:\n        if name in self.__dict__:\n            return self.__dict__[name]\n        for cls in self.__class__.__mro__:\n            if name in cls.__dict__:\n                return cls.__dict__[name]\n        return super().getattr(name, default)  # type: ignore[misc]\n    except AttributeError:\n        if default is not Null:\n            return default\n        raise 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 Self

Alias:

  • cuda

Examples:

Python Console Session
>>> import torch\n>>> d = FlatDict(a=torch.tensor(1))\n>>> d.gpu().dict()\n{'a': tensor(1, device='cuda:0')}\n>>> d.cuda().dict()  # alias\n{'a': tensor(1, device='cuda:0')}\n
Source code in chanfig/flat_dict.py Python
def gpu(self) -> Self:  # pragma: no cover\n    r\"\"\"\n    Move all tensors to gpu.\n\n    Returns:\n        self:\n\n    **Alias**:\n\n    + `cuda`\n\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    \"\"\"\n\n    return 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:\n    r\"\"\"\n    Determine if an attribute exists in `FlatDict`.\n\n    Args:\n        name:\n\n    Returns:\n        (bool):\n\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    \"\"\"\n\n    try:\n        if name in self.__dict__ or name in self.__class__.__dict__:\n            return True\n        return super().hasattr(name)  # type: ignore[misc]\n    except AttributeError:\n        return 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) -> Self:\n    r\"\"\"\n    Alias of [`intersect`][chanfig.FlatDict.intersect].\n    \"\"\"\n    return self.intersect(other, *args, **kwargs)\n
"},{"location":"flat_dict/#chanfig.FlatDict.interpolate","title":"interpolate(use_variable=True, interpolators=None, unsafe_eval=False)","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 unsafe_eval bool

Whether to evaluate interpolated values.

False

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(unsafe_eval=True).dict()\n{'a': 1, 'b': 1, 'c': 1.1}\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)\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(  # pylint: disable=R0912\n    self, use_variable: bool = True, interpolators: MutableMapping | None = None, unsafe_eval: bool = False\n) -> Self:\n    r\"\"\"\n    Perform Variable interpolation.\n\n    Variable interpolation allows you to set the value of one key to be the value of another key easily.\n\n    Args:\n        use_variable: Whether to convert values to `Variable` objects.\n        interpolators: Mapping contains values for interpolation. Defaults to `self`.\n        unsafe_eval: Whether to evaluate interpolated values.\n\n    Raises:\n        ValueError: If value is not interpolatable.\n        ValueError: If reference to itself.\n        ValueError: If has circular reference.\n\n    See Also:\n        [Variable][`chanfig.Variable`]: Mutable wrapper of immutable objects.\n\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(unsafe_eval=True).dict()\n        {'a': 1, 'b': 1, 'c': 1.1}\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    \"\"\"\n    # pylint: disable=C0103\n\n    interpolators = interpolators or self\n    placeholders: dict[str, list[str]] = {}\n    for key, value in self.all_items():\n        if isinstance(value, list):\n            for v in value:\n                self.find_placeholders(key, v, placeholders)\n        elif isinstance(value, Mapping):\n            for v in value.values():\n                self.find_placeholders(key, v, placeholders)\n        else:\n            self.find_placeholders(key, value, placeholders)\n    circular_references = find_circular_reference(placeholders)\n    if circular_references:\n        raise ValueError(f\"Circular reference found: {'->'.join(circular_references)}.\")\n    if use_variable:\n        placeholder_names = {i for j in placeholders.values() for i in j}\n        for name in list(placeholder_names.difference(placeholders.keys())):\n            if name not in interpolators:\n                raise ValueError(f\"{name} is not found in {interpolators}.\")\n            if not isinstance(interpolators[name], Variable):\n                interpolators[name] = Variable(interpolators[name])\n    for key, value in placeholders.items():\n        if isinstance(self[key], list):\n            for index, v in enumerate(self[key]):\n                self[key][index] = self.substitute(v, interpolators, value)\n        elif isinstance(self[key], Mapping):\n            for k, v in self[key].items():\n                self[key][k] = self.substitute(v, interpolators, value)\n        else:\n            self[key] = self.substitute(self[key], interpolators, value)\n        if unsafe_eval and isinstance(self[key], str):\n            with suppress(SyntaxError):\n                self[key] = eval(self[key])  # pylint: disable=W0123\n    return 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:

  • inter

Examples:

Python Console Session
>>> d = FlatDict(a=1, b=2, c=3)\n>>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d.intersect(n).dict()\n{}\n>>> l = [('c', 3), ('d', 4)]\n>>> d.intersect(l).dict()\n{'c': 3}\n>>> d.merge(l).intersect(\"tests/test.yaml\").dict()\n{'a': 1, 'b': 2, 'c': 3}\n>>> d.intersect(1)\nTraceback (most recent call last):\nTypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.\n>>> d.inter(FlatDict(b='b', c='c', d='d')).dict()  # alias\n{}\n
Source code in chanfig/flat_dict.py Python
def intersect(self, other: Mapping | Iterable | PathStr) -> Self:\n    r\"\"\"\n    Intersection of `FlatDict` and `other`.\n\n    Args:\n        other (Mapping | Iterable | PathStr):\n\n    Returns:\n        (FlatDict):\n\n    **Alias**:\n\n    + `inter`\n\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    \"\"\"\n\n    if isinstance(other, (PathLike, str, bytes)):\n        other = self.load(other)\n    if isinstance(other, (Mapping,)):\n        other = self.empty(other).items()\n    if not isinstance(other, Iterable):\n        raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n    return 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:\n    r\"\"\"\n    Dump `FlatDict` to json file.\n\n    This method internally calls `self.jsons()` to generate json string.\n    You may overwrite `jsons` in case something is not json serializable.\n\n    Examples:\n        >>> d = FlatDict(a=1, b=2, c=3)\n        >>> d.json(\"tests/test.json\")\n    \"\"\"\n\n    with self.open(file, mode=\"w\") as fp:  # pylint: disable=C0103\n        fp.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:\n    r\"\"\"\n    Dump `FlatDict` to json string.\n\n    Returns:\n        (str):\n\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    \"\"\"\n\n    kwargs.setdefault(\"cls\", JsonEncoder)\n    kwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\n    return 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

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\n    cls, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n) -> Self:\n    \"\"\"\n    Load `FlatDict` from file.\n\n    Args:\n        file: File to load from.\n        method: File type, should be in `JSON` or `YAML`.\n\n    Returns:\n        (FlatDict):\n\n    Raises:\n        ValueError: If load from `IO` and `method` is not specified.\n        TypeError: If dump to unsupported extension.\n\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    \"\"\"\n\n    if method is None:\n        if isinstance(file, (IOBase, IO)):\n            raise ValueError(\"`method` must be specified when loading from IO.\")\n        method = splitext(file)[-1][1:]\n    extension = method.lower()\n    if extension in JSON:\n        return cls.from_json(file, *args, **kwargs)\n    if extension in YAML:\n        return cls.from_yaml(file, *args, **kwargs)\n    raise 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 Self

Alias:

  • union

Examples:

Python Console Session
>>> d = FlatDict(a=1, b=2, c=3)\n>>> n = {'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d.merge(n).dict()\n{'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n>>> l = [('c', 3), ('d', 4)]\n>>> d.merge(l).dict()\n{'a': 1, 'b': 'b', 'c': 3, 'd': 4}\n>>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict()  # alias\n{'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}\n>>> d = FlatDict()\n>>> d.merge({1: 1, 2: 2, 3:3}).dict()\n{1: 1, 2: 2, 3: 3}\n>>> d.merge(d.clone()).dict()\n{1: 1, 2: 2, 3: 3}\n>>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict()\n{1: 1, 2: 2, 3: 3, 4: 4, 5: 5}\n
Source code in chanfig/flat_dict.py Python
def merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> Self:\n    r\"\"\"\n    Merge `other` into `FlatDict`.\n\n    Args:\n        *args: `Mapping` or `Sequence` to be merged.\n        overwrite: Whether to overwrite existing values.\n        **kwargs: `Mapping` to be merged.\n\n    Returns:\n        self:\n\n    **Alias**:\n\n    + `union`\n\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    \"\"\"\n\n    if len(args) == 1:\n        args = args[0]\n        if isinstance(args, (PathLike, str, bytes)):\n            args = self.load(args)  # type: ignore[assignment]\n            warn(\n                \"merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.\",\n                PendingDeprecationWarning,\n            )\n        self._merge(self, args, overwrite=overwrite)\n    elif len(args) > 1:\n        self._merge(self, args, overwrite=overwrite)\n    if kwargs:\n        self._merge(self, kwargs, overwrite=overwrite)\n    return 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 Self

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) -> Self:\n    r\"\"\"\n    Merge content of `file` into `FlatDict`.\n\n    Args:\n        file (File):\n        *args: Passed to [`load`][chanfig.FlatDict.load].\n        **kwargs: Passed to [`load`][chanfig.FlatDict.load].\n\n    Returns:\n        self:\n\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    \"\"\"\n\n    return 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\n>>> io = open(\"tests/test.yaml\")\n>>> with FlatDict.open(io) as fp:\n...     print(fp.read())\na: 1\nb: 2\nc: 3\n\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]:\n    r\"\"\"\n    Open file IO from file path or IO.\n\n    This methods extends the ability of built-in `open` by allowing it to accept an `IOBase` object.\n\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\n    Yields:\n        (Generator[IOBase | IO, Any, Any]):\n\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    \"\"\"\n\n    if isinstance(file, (IOBase, IO)):\n        yield file\n    elif isinstance(file, (PathLike, str, bytes)):\n        try:\n            file = open(file, *args, encoding=encoding, **kwargs)  # type: ignore[call-overload] # noqa: SIM115\n            yield file  # type: ignore[misc]\n        finally:\n            with suppress(Exception):\n                file.close()  # type: ignore[union-attr]\n    else:\n        raise 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:

  • save

Examples:

Python Console Session
>>> d = FlatDict(a=1, b=2, c=3)\n>>> d.save(\"tests/test.yaml\")\n>>> d.save(\"test.conf\")\nTraceback (most recent call last):\nTypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n>>> with open(\"test.yaml\", \"w\") as f:\n...     d.save(f)\nTraceback (most recent call last):\nValueError: `method` must be specified when saving to IO.\n
Source code in chanfig/flat_dict.py Python
def save(  # pylint: disable=W1113\n    self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n) -> None:\n    r\"\"\"\n    Save `FlatDict` to file.\n\n    Raises:\n        ValueError: If save to `IO` and `method` is not specified.\n        TypeError: If save to unsupported extension.\n\n    **Alias**:\n\n    + `save`\n\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    \"\"\"\n\n    if method is None:\n        if isinstance(file, (IOBase, IO)):\n            raise ValueError(\"`method` must be specified when saving to IO.\")\n        method = splitext(file)[-1][1:]\n    extension = method.lower()\n    if extension in YAML:\n        return self.yaml(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026\n    if extension in JSON:\n        return self.json(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026\n    raise 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:\n    r\"\"\"\n    Set value of `FlatDict`.\n\n    Args:\n        name:\n        value:\n\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    \"\"\"\n\n    if name is Null:\n        raise ValueError(\"name must not be null\")\n    if name in self and isinstance(self.get(name), Variable):\n        self.get(name).set(value)\n    else:\n        dict.__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:\n    r\"\"\"\n    Set attribute of `FlatDict`.\n\n    Note that it won't alter values in `FlatDict`.\n\n    Args:\n        name:\n        value:\n\n    Warns:\n        RuntimeWarning: If name already exists in `FlatDict`.\n\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    \"\"\"\n\n    if name in self:\n        warn(\n            f\"{name} already exists in {self.__class__.__name__}.\\n\"\n            f\"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.\",\n            RuntimeWarning,\n        )\n    self.__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) -> Self:\n    r\"\"\"\n    Sort `FlatDict`.\n\n    Returns:\n        (FlatDict):\n\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    \"\"\"\n\n    items = sorted(self.items(), key=key, reverse=reverse)\n    self.clear()\n    for k, v in items:  # pylint: disable=C0103\n        self[k] = v\n    return 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 Self

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) -> Self:  # pragma: no cover\n    r\"\"\"\n    Convert values of `FlatDict` to target `cls`.\n\n    Args:\n        cls (str | torch.device | torch.dtype):\n\n    Returns:\n        self:\n\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\n    # pylint: disable=C0103\n\n    if isinstance(cls, (str, TorchDevice, TorchDType)):\n        for k, v in self.all_items():\n            if hasattr(v, \"to\"):\n                self[k] = v.to(cls)\n        return self\n\n    raise TypeError(f\"to() only support torch.dtype and torch.device, but got {cls}.\")\n
"},{"location":"flat_dict/#chanfig.FlatDict.to_dict","title":"to_dict(flatten=False)","text":"

Alias of dict.

Source code in chanfig/flat_dict.py Python
def to_dict(self, flatten: bool = False) -> Mapping | Sequence | Set:\n    r\"\"\"\n    Alias of [`dict`][chanfig.FlatDict.dict].\n    \"\"\"\n\n    return self.dict(flatten)\n
"},{"location":"flat_dict/#chanfig.FlatDict.tpu","title":"tpu()","text":"

Move all tensors to tpu.

Returns:

Name Type Description self Self

Alias:

  • xla

Examples:

Python Console Session
>>> import torch\n>>> d = FlatDict(a=torch.tensor(1))\n>>> d.tpu().dict()\n{'a': tensor(1, device='xla:0')}\n>>> d.xla().dict()  # alias\n{'a': tensor(1, device='xla:0')}\n
Source code in chanfig/flat_dict.py Python
def tpu(self) -> Self:  # pragma: no cover\n    r\"\"\"\n    Move all tensors to tpu.\n\n    Returns:\n        self:\n\n    **Alias**:\n\n    + `xla`\n\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    \"\"\"\n\n    return 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) -> Self:\n    r\"\"\"\n    Alias of [`merge`][chanfig.FlatDict.merge].\n    \"\"\"\n    return 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:\n    r\"\"\"\n    Validate `FlatDict`.\n\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\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    \"\"\"\n\n    self._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) -> Self:  # pragma: no cover\n    r\"\"\"\n    Alias of [`tpu`][chanfig.FlatDict.tpu].\n    \"\"\"\n    return 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:\n    r\"\"\"\n    Dump `FlatDict` to yaml file.\n\n    This method internally calls `self.yamls()` to generate yaml string.\n    You may overwrite `yamls` in case something is not yaml serializable.\n\n    Examples:\n        >>> d = FlatDict(a=1, b=2, c=3)\n        >>> d.yaml(\"tests/test.yaml\")\n    \"\"\"\n\n    with self.open(file, mode=\"w\") as fp:  # pylint: disable=C0103\n        self.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:\n    r\"\"\"\n    Dump `FlatDict` to yaml string.\n\n    Returns:\n        (str):\n\n    Examples:\n        >>> FlatDict(a=1, b=2, c=3).yamls()\n        'a: 1\\nb: 2\\nc: 3\\n'\n    \"\"\"\n\n    kwargs.setdefault(\"Dumper\", YamlDumper)\n    kwargs.setdefault(\"indent\", self.getattr(\"indent\", 2))\n    return yaml_dump(self.dict(), *args, **kwargs)\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 | Sequence | Set

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, flatten: bool = False) -> Mapping | Sequence | Set:\n    r\"\"\"\n    Convert an object to a dict.\n\n    Note that when converting a `set` object, it may be converted to a `tuple` object if its values is not hashable.\n\n    Args:\n        obj: Object to be converted.\n\n    Returns:\n        A dict.\n\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    \"\"\"\n\n    if flatten and isinstance(obj, FlatDict):\n        return {k: to_dict(v) for k, v in obj.all_items()}\n    if isinstance(obj, Mapping):\n        return {k: to_dict(v) for k, v in obj.items()}\n    if isinstance(obj, list):\n        return [to_dict(v) for v in obj]\n    if isinstance(obj, tuple):\n        return tuple(to_dict(v) for v in obj)\n    if isinstance(obj, set):\n        try:\n            return {to_dict(v) for v in obj}\n        except TypeError:\n            return tuple(to_dict(v) for v in obj)\n    if isinstance(obj, Variable):\n        return obj.value\n    if is_dataclass(obj):\n        return asdict(obj)\n    if hasattr(obj, \"to_dict\"):\n        return obj.to_dict()\n    return 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:

  • save

Examples:

Python Console Session
>>> obj = {\"a\": 1, \"b\": 2, \"c\": 3}\n>>> save(obj, \"test.yaml\")\n>>> save(obj, \"test.json\")\n>>> save(obj, \"test.conf\")\nTraceback (most recent call last):\nTypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.\n>>> with open(\"test.yaml\", \"w\") as f:\n...     save(obj, f)\nTraceback (most recent call last):\nValueError: `method` must be specified when saving to IO.\n
Source code in chanfig/functional.py Python
def save(  # pylint: disable=W1113\n    obj, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]\n) -> None:\n    r\"\"\"\n    Save `FlatDict` to file.\n\n    Raises:\n        ValueError: If save to `IO` and `method` is not specified.\n        TypeError: If save to unsupported extension.\n\n    **Alias**:\n\n    + `save`\n\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    \"\"\"\n\n    if isinstance(obj, FlatDict):\n        obj.save(file, method, *args, **kwargs)\n        return\n\n    data = to_dict(obj)\n    if method is None:\n        if isinstance(file, IOBase):\n            raise ValueError(\"`method` must be specified when saving to IO.\")\n        method = splitext(file)[-1][1:]\n    extension = method.lower()\n    if extension in YAML:\n        with FlatDict.open(file, mode=\"w\") as fp:  # pylint: disable=C0103\n            yaml_dump(data, fp, *args, **kwargs)\n        return\n    if extension in JSON:\n        with FlatDict.open(file, mode=\"w\") as fp:  # pylint: disable=C0103\n            fp.write(json_dumps(data, *args, **kwargs))\n        return\n    raise TypeError(f\"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.\")\n

options: heading_level: 0

Load a file into a FlatDict.

This function simply calls cls.load, by default, cls is NestedDict.

Parameters:

Name Type Description Default file PathStr

The file to load.

required cls type[FlatDict]

The class of the file to load. Defaults to NestedDict.

NestedDict *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(  # pylint: disable=W1113\n    file: PathStr, cls: type[FlatDict] = NestedDict, *args: Any, **kwargs: Any\n) -> FlatDict:\n    r\"\"\"\n    Load a file into a `FlatDict`.\n\n    This function simply calls `cls.load`, by default, `cls` is `NestedDict`.\n\n    Args:\n        file: The file to load.\n        cls: The class of the file to load. Defaults to `NestedDict`.\n        *args: The arguments to pass to `NestedDict.load`.\n        **kwargs: The keyword arguments to pass to `NestedDict.load`.\n\n    See Also:\n        [`load`][chanfig.FlatDict.load]\n\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    \"\"\"\n\n    return cls.load(file, *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 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:\n    r\"\"\"\n    Apply `func` to all children of `obj`.\n\n    Note that this method is meant for non-in-place modification of `obj` and should return the original object.\n\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\n    Returns:\n        (Any): Return value of `func`.\n\n    See Also:\n        [`apply_`][chanfig.nested_dict.apply_]: Apply an in-place operation.\n    \"\"\"\n\n    if isinstance(obj, NestedDict):\n        return obj.empty_like(**{k: apply(v, func, *args, **kwargs) for k, v in obj.items()})\n    if isinstance(obj, Mapping):\n        return {k: apply(v, func, *args, **kwargs) for k, v in obj.items()}\n    if isinstance(obj, list):\n        return [apply(v, func, *args, **kwargs) for v in obj]\n    if isinstance(obj, tuple):\n        return tuple(apply(v, func, *args, **kwargs) for v in obj)\n    if isinstance(obj, set):\n        try:\n            return {apply(v, func, *args, **kwargs) for v in obj}\n        except TypeError:\n            tuple(apply(v, func, *args, **kwargs) for v in obj)\n    return 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:\n    r\"\"\"\n    Apply `func` to all children of `obj`.\n\n    Note that this method is meant for non-in-place modification of `obj` and should return a new object.\n\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\n    Returns:\n        (Any): Return value of `func`.\n\n    See Also:\n        [`apply_`][chanfig.nested_dict.apply]: Apply a non-in-place operation.\n    \"\"\"\n    # pylint: disable=C0103\n\n    if isinstance(obj, Mapping):\n        for v in obj.values():\n            apply_(v, func, *args, **kwargs)\n    if isinstance(obj, (list, tuple, set)):\n        for v in obj:\n            apply_(v, func, *args, **kwargs)\n    return 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>>> NestedDict({\"i.d\": [{'c': 1013}, {'k': 1031}]})\nNestedDict(\n  ('i'): NestedDict(\n    ('d'): [NestedDict(\n      ('c'): 1013\n    ), NestedDict(\n      ('k'): 1031\n    )]\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):  # pylint: disable=E1136\n    r\"\"\"\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\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\n    This behaviour allows you to pass keyword arguments to other functions as easy as `func1(**d.func1)`.\n\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\n    `NestedDict` also introduce `all_keys`, `all_values`, `all_items` methods to get all keys, values, items\n    respectively in the nested structure.\n\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\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\n        `convert_mapping` is automatically applied to arguments during initialisation.\n\n    Examples:\n        >>> NestedDict({\"f.n\": \"chang\"})\n        NestedDict(\n          ('f'): NestedDict(\n            ('n'): 'chang'\n          )\n        )\n        >>> NestedDict({\"i.d\": [{'c': 1013}, {'k': 1031}]})\n        NestedDict(\n          ('i'): NestedDict(\n            ('d'): [NestedDict(\n              ('c'): 1013\n            ), NestedDict(\n              ('k'): 1031\n            )]\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    \"\"\"\n\n    convert_mapping: bool = False\n    delimiter: str = \".\"\n    fallback: bool = False\n\n    def __init__(\n        self,\n        *args: Any,\n        default_factory: Callable | None = None,\n        convert_mapping: bool | None = None,\n        fallback: bool | None = None,\n        **kwargs: Any,\n    ) -> None:\n        super().__init__(default_factory)\n        self.merge(*args, **kwargs)\n        if convert_mapping is not None:\n            self.setattr(\"convert_mapping\", convert_mapping)\n        if fallback is not None:\n            self.setattr(\"fallback\", fallback)\n\n    def all_keys(self) -> Generator:\n        r\"\"\"\n        Get all keys of `NestedDict`.\n\n        Returns:\n            (Generator):\n\n        Examples:\n            >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n            >>> list(d.all_keys())\n            ['a', 'b.c', 'b.d']\n        \"\"\"\n\n        delimiter = self.getattr(\"delimiter\", \".\")\n\n        @wraps(self.all_keys)\n        def all_keys(self, prefix=Null):\n            for key, value in self.items():\n                if prefix is not Null:\n                    key = str(prefix) + str(delimiter) + str(key)\n                if isinstance(value, NestedDict):\n                    yield from all_keys(value, key)\n                else:\n                    yield key\n\n        return all_keys(self)\n\n    def all_values(self) -> Generator:\n        r\"\"\"\n        Get all values of `NestedDict`.\n\n        Returns:\n            (Generator):\n\n        Examples:\n            >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n            >>> list(d.all_values())\n            [1, 2, 3]\n        \"\"\"\n\n        for value in self.values():\n            if isinstance(value, NestedDict):\n                yield from value.all_values()\n            else:\n                yield value\n\n    def all_items(self) -> Generator:\n        r\"\"\"\n        Get all items of `NestedDict`.\n\n        Returns:\n            (Generator):\n\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        \"\"\"\n\n        delimiter = self.getattr(\"delimiter\", \".\")\n\n        @wraps(self.all_items)\n        def all_items(self, prefix=Null):\n            for key, value in self.items():\n                if prefix is not Null:\n                    key = str(prefix) + str(delimiter) + str(key)\n                if isinstance(value, NestedDict):\n                    yield from all_items(value, key)\n                else:\n                    yield key, value\n\n        return all_items(self)\n\n    def apply(self, func: Callable, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Recursively apply a function to `NestedDict` and its children.\n\n        Note:\n            This method is meant for non-in-place modification of `obj`, for example, [`to`][chanfig.NestedDict.to].\n\n        Args:\n            func (Callable):\n\n        See Also:\n            [`apply_`][chanfig.NestedDict.apply_]: Apply an in-place operation.\n            [`apply`][chanfig.nested_dict.apply]: Implementation of `apply`.\n\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        \"\"\"\n\n        return apply(self, func, *args, **kwargs)\n\n    def apply_(self, func: Callable, *args: Any, **kwargs: Any) -> Self:\n        r\"\"\"\n        Recursively apply a function to `NestedDict` and its children.\n\n        Note:\n            This method is meant for in-place modification of `obj`, for example, [`freeze`][chanfig.Config.freeze].\n\n        Args:\n            func (Callable):\n\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\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        \"\"\"\n\n        apply_(self, func, *args, **kwargs)\n        return self\n\n    def get(self, name: Any, default: Any = None, fallback: bool | None = None) -> Any:\n        r\"\"\"\n        Get value from `NestedDict`.\n\n        Note that `default` has higher priority than `default_factory`.\n\n        Args:\n            name:\n            default:\n\n        Returns:\n            value:\n                If `NestedDict` does not contain `name`, return `default`.\n                If `default` is not specified, return `default_factory()`.\n\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\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        \"\"\"\n\n        delimiter = self.getattr(\"delimiter\", \".\")\n        if fallback is None:\n            fallback = self.getattr(\"fallback\", False)\n        fallback_name = name.split(delimiter)[-1] if isinstance(name, str) else name\n        fallback_value = Null\n        try:\n            while isinstance(name, str) and delimiter in name:\n                if fallback and fallback_name in self:\n                    fallback_value = self.get(fallback_name)\n                name, rest = name.split(delimiter, 1)\n                self, name = self[name], rest  # pylint: disable=W0642\n        except (KeyError, AttributeError, TypeError):\n            if fallback and fallback_value is not Null:\n                return fallback_value\n            if default is not Null:\n                return default\n            raise KeyError(name) from None\n        if (fallback and fallback_value is not Null) and (not isinstance(self, Iterable) or name not in self):\n            return fallback_value\n        # if value is a python dict\n        if not isinstance(self, NestedDict):\n            if name not in self and default is not Null:\n                return default\n            return self[name]\n        return super().get(name, default)\n\n    def set(  # pylint: disable=W0221\n        self,\n        name: Any,\n        value: Any,\n        convert_mapping: bool | None = None,\n    ) -> None:\n        r\"\"\"\n        Set value of `NestedDict`.\n\n        Args:\n            name:\n            value:\n            convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n                Defaults to self.convert_mapping.\n\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\n\n        full_name = name\n        delimiter = self.getattr(\"delimiter\", \".\")\n        if convert_mapping is None:\n            convert_mapping = self.getattr(\"convert_mapping\", False)\n        default_factory = self.getattr(\"default_factory\", self.empty) or self.empty\n        try:\n            while isinstance(name, str) and delimiter in name:\n                name, rest = name.split(delimiter, 1)\n                if name in dir(self) and isinstance(getattr(self.__class__, name), (property, cached_property)):\n                    self, name = getattr(self, name), rest\n                elif name not in self and isinstance(self, Mapping):\n                    default = (\n                        self.__missing__(name, default_factory()) if hasattr(self, \"__missing__\") else default_factory()\n                    )\n                    self, name = default, rest\n                else:\n                    self, name = self[name], rest\n                if isinstance(self, NestedDict):\n                    default_factory = self.getattr(\"default_factory\", self.empty) or self.empty\n        except (AttributeError, TypeError):\n            raise KeyError(name) from None\n\n        if (\n            convert_mapping\n            and not isinstance(value, default_factory if isinstance(default_factory, type) else type(self))\n            and not isinstance(value, Variable)\n        ):\n            if isinstance(value, Mapping):\n                try:\n                    value = default_factory(**value)\n                except TypeError:\n                    value = default_factory(value)\n            if isinstance(value, list):\n                value = [default_factory(v) if isinstance(v, Mapping) else v for v in value]\n            if isinstance(value, tuple):\n                value = tuple(default_factory(v) if isinstance(v, Mapping) else v for v in value)\n            if isinstance(value, set):\n                value = {default_factory(v) if isinstance(v, Mapping) else v for v in list(value)}\n        if isinstance(self, NestedDict):\n            super().set(name, value)\n        elif isinstance(self, Mapping):\n            dict.__setitem__(self, name, value)\n        else:\n            raise ValueError(\n                f\"Cannot set `{full_name}` to `{value}`, as `{delimiter.join(full_name.split(delimiter)[:-1])}={self}`.\"\n            )\n\n    def delete(self, name: Any) -> None:\n        r\"\"\"\n        Delete value from `NestedDict`.\n\n        Args:\n            name:\n\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        \"\"\"\n\n        delimiter = self.getattr(\"delimiter\", \".\")\n        try:\n            while isinstance(name, str) and delimiter in name:\n                name, rest = name.split(delimiter, 1)\n                self, name = self[name], rest  # pylint: disable=W0642\n        except (AttributeError, TypeError):\n            raise KeyError(name) from None\n        # if value is a python dict\n        if not isinstance(self, NestedDict):\n            del self[name]\n            return\n        super().delete(name)\n\n    def pop(self, name: Any, default: Any = Null) -> Any:\n        r\"\"\"\n        Pop value from `NestedDict`.\n\n        Args:\n            name:\n            default:\n\n        Returns:\n            value: If `NestedDict` does not contain `name`, return `default`.\n\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        \"\"\"\n\n        delimiter = self.getattr(\"delimiter\", \".\")\n        try:\n            while isinstance(name, str) and delimiter in name:\n                name, rest = name.split(delimiter, 1)\n                self, name = self[name], rest  # pylint: disable=W0642\n        except (AttributeError, TypeError):\n            raise KeyError(name) from None\n        if not isinstance(self, dict) or name not in self:\n            if default is not Null:\n                return default\n            raise KeyError(name)\n        return super().pop(name)\n\n    def setdefault(  # type: ignore[override]  # pylint: disable=R0912,W0221\n        self,\n        name: Any,\n        value: Any,\n        convert_mapping: bool | None = None,\n    ) -> Any:\n        r\"\"\"\n        Set default value for `NestedDict`.\n\n        Args:\n            name:\n            value:\n            convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n                Defaults to self.convert_mapping.\n\n        Returns:\n            value: If `NestedDict` does not contain `name`, return `value`.\n\n        Examples:\n            >>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\", \"n.a.b.c\": 1})\n            >>> d.setdefault(\"d.i\", 1031)\n            1031\n            >>> d.setdefault(\"i.d\", \"chang\")\n            1013\n            >>> d.setdefault(\"f.n\", 1013)\n            'chang'\n            >>> d.setdefault(\"n.a.b.d\", 2)\n            2\n        \"\"\"\n        # pylint: disable=W0642\n\n        full_name = name\n        delimiter = self.getattr(\"delimiter\", \".\")\n        if convert_mapping is None:\n            convert_mapping = self.getattr(\"convert_mapping\", False)\n        default_factory = self.getattr(\"default_factory\", self.empty) or self.empty\n        try:\n            while isinstance(name, str) and delimiter in name:\n                name, rest = name.split(delimiter, 1)\n                if name in dir(self) and isinstance(getattr(self.__class__, name), (property, cached_property)):\n                    self, name = getattr(self, name), rest\n                elif name not in self and isinstance(self, Mapping):\n                    default = (\n                        self.__missing__(name, default_factory()) if hasattr(self, \"__missing__\") else default_factory()\n                    )\n                    self, name = default, rest\n                else:\n                    self, name = self[name], rest\n                if isinstance(self, NestedDict):\n                    default_factory = self.getattr(\"default_factory\", self.empty) or self.empty\n        except (AttributeError, TypeError):\n            raise KeyError(name) from None\n\n        if isinstance(self, NestedDict) and name in self:\n            return super().get(name)\n        elif isinstance(self, Mapping) and name in self:\n            dict.__getitem__(self, name)\n\n        if (\n            convert_mapping\n            and isinstance(value, Mapping)\n            and not isinstance(value, default_factory if isinstance(default_factory, type) else type(self))\n            and not isinstance(value, Variable)\n        ):\n            try:\n                value = default_factory(**value)\n            except TypeError:\n                value = default_factory(value)\n        if isinstance(self, NestedDict):\n            super().set(name, value)\n        elif isinstance(self, Mapping):\n            dict.__setitem__(self, name, value)\n        else:\n            raise ValueError(\n                f\"Cannot set `{full_name}` to `{value}`, as `{delimiter.join(full_name.split(delimiter)[:-1])}={self}`.\"\n            )\n        return value\n\n    def validate(self) -> None:\n        r\"\"\"\n        Validate `NestedDict`.\n\n        Raises:\n            TypeError: If `Variable` has invalid type.\n            ValueError: If `Variable` has invalid value.\n\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        \"\"\"\n\n        self.apply_(self._validate)\n\n    def sort(self, key: Callable | None = None, reverse: bool = False, recursive: bool = True) -> Self:\n        r\"\"\"\n        Sort `NestedDict`.\n\n        Args:\n            recursive (bool): Whether to apply `sort` recursively.\n\n        Returns:\n            (NestedDict):\n\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]\n        \"\"\"\n\n        if recursive:\n            for value in self.values():\n                if isinstance(value, FlatDict):\n                    value.sort(key=key, reverse=reverse)\n        return super().sort(key=key, reverse=reverse)\n\n    @staticmethod\n    def _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping:\n        if not that:\n            return this\n        if isinstance(that, Mapping):\n            that = that.items()\n        with this.converting() if isinstance(this, NestedDict) else nullcontext():\n            for key, value in that:\n                if key in this and isinstance(this[key], Mapping):\n                    if isinstance(value, Mapping):\n                        NestedDict._merge(this[key], value, overwrite)\n                    elif overwrite:\n                        if isinstance(this, NestedDict):\n                            this.set(key, value)\n                        else:\n                            this[key] = value\n                elif key in dir(this) and isinstance(getattr(this.__class__, key, None), (property, cached_property)):\n                    if isinstance(getattr(this, key, None), FlatDict):\n                        getattr(this, key).merge(value, overwrite=overwrite)\n                    else:\n                        setattr(this, key, value)\n                elif overwrite or key not in this:\n                    if isinstance(this, NestedDict):\n                        this.set(key, value)\n                    else:\n                        this[key] = value\n        return this\n\n    def intersect(self, other: Mapping | Iterable | PathStr, recursive: bool = True) -> Self:  # pylint: disable=W0221\n        r\"\"\"\n        Intersection of `NestedDict` and `other`.\n\n        Args:\n            other (Mapping | Iterable | PathStr):\n            recursive (bool):\n\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        \"\"\"\n\n        if isinstance(other, (PathLike, str, bytes)):\n            other = self.load(other)\n        if isinstance(other, (Mapping,)):\n            other = self.empty(other).items()\n        if not isinstance(other, Iterable):\n            raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n        return self.empty(self._intersect(self, other, recursive))\n\n    @staticmethod\n    def _intersect(this: NestedDict, that: Iterable, recursive: bool = True) -> Mapping:\n        ret: NestedDict = NestedDict()\n        for key, value in that:\n            if key in this:\n                if isinstance(this[key], NestedDict) and isinstance(value, Mapping) and recursive:\n                    intersects = this[key].intersect(value)\n                    if intersects:\n                        ret[key] = intersects\n                elif this[key] == value:\n                    ret[key] = value\n        return ret\n\n    def difference(  # pylint: disable=W0221, C0103\n        self, other: Mapping | Iterable | PathStr, recursive: bool = True\n    ) -> Self:\n        r\"\"\"\n        Difference between `NestedDict` and `other`.\n\n        Args:\n            other (Mapping | Iterable | PathStr):\n            recursive (bool):\n\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        \"\"\"\n\n        if isinstance(other, (PathLike, str, bytes)):\n            other = self.load(other)\n        if isinstance(other, (Mapping,)):\n            other = self.empty(other).items()\n        if not isinstance(other, Iterable):\n            raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n        return self.empty(self._difference(self, other, recursive))\n\n    @staticmethod\n    def _difference(this: NestedDict, that: Iterable, recursive: bool = True) -> Mapping:\n        ret: NestedDict = NestedDict()\n        for key, value in that:\n            if key not in this:\n                ret[key] = value\n            elif isinstance(this[key], NestedDict) and isinstance(value, Mapping) and recursive:\n                differences = this[key].difference(value)\n                if differences:\n                    ret[key] = differences\n            elif this[key] != value:\n                ret[key] = value\n        return ret\n\n    @contextmanager\n    def converting(self):\n        convert_mapping = self.getattr(\"convert_mapping\", False)\n        try:\n            self.setattr(\"convert_mapping\", True)\n            yield\n        finally:\n            self.setattr(\"convert_mapping\", convert_mapping)\n\n    def __contains__(self, name: Any) -> bool:\n        delimiter = self.getattr(\"delimiter\", \".\")\n        try:\n            while isinstance(name, str) and delimiter in name:\n                name, rest = name.split(delimiter, 1)\n                if super().__contains__(name):\n                    self, name = self[name], rest  # pylint: disable=W0642\n                else:\n                    return False\n            return super().__contains__(name)\n        except (TypeError, KeyError):  # TypeError when name is not in self\n            return 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:\n    r\"\"\"\n    Get all items of `NestedDict`.\n\n    Returns:\n        (Generator):\n\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    \"\"\"\n\n    delimiter = self.getattr(\"delimiter\", \".\")\n\n    @wraps(self.all_items)\n    def all_items(self, prefix=Null):\n        for key, value in self.items():\n            if prefix is not Null:\n                key = str(prefix) + str(delimiter) + str(key)\n            if isinstance(value, NestedDict):\n                yield from all_items(value, key)\n            else:\n                yield key, value\n\n    return 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:\n    r\"\"\"\n    Get all keys of `NestedDict`.\n\n    Returns:\n        (Generator):\n\n    Examples:\n        >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n        >>> list(d.all_keys())\n        ['a', 'b.c', 'b.d']\n    \"\"\"\n\n    delimiter = self.getattr(\"delimiter\", \".\")\n\n    @wraps(self.all_keys)\n    def all_keys(self, prefix=Null):\n        for key, value in self.items():\n            if prefix is not Null:\n                key = str(prefix) + str(delimiter) + str(key)\n            if isinstance(value, NestedDict):\n                yield from all_keys(value, key)\n            else:\n                yield key\n\n    return 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:\n    r\"\"\"\n    Get all values of `NestedDict`.\n\n    Returns:\n        (Generator):\n\n    Examples:\n        >>> d = NestedDict({'a': 1, 'b': {'c': 2, 'd': 3}})\n        >>> list(d.all_values())\n        [1, 2, 3]\n    \"\"\"\n\n    for value in self.values():\n        if isinstance(value, NestedDict):\n            yield from value.all_values()\n        else:\n            yield 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) -> Self:\n    r\"\"\"\n    Recursively apply a function to `NestedDict` and its children.\n\n    Note:\n        This method is meant for non-in-place modification of `obj`, for example, [`to`][chanfig.NestedDict.to].\n\n    Args:\n        func (Callable):\n\n    See Also:\n        [`apply_`][chanfig.NestedDict.apply_]: Apply an in-place operation.\n        [`apply`][chanfig.nested_dict.apply]: Implementation of `apply`.\n\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    \"\"\"\n\n    return 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) -> Self:\n    r\"\"\"\n    Recursively apply a function to `NestedDict` and its children.\n\n    Note:\n        This method is meant for in-place modification of `obj`, for example, [`freeze`][chanfig.Config.freeze].\n\n    Args:\n        func (Callable):\n\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\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    \"\"\"\n\n    apply_(self, func, *args, **kwargs)\n    return 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:\n    r\"\"\"\n    Delete value from `NestedDict`.\n\n    Args:\n        name:\n\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    \"\"\"\n\n    delimiter = self.getattr(\"delimiter\", \".\")\n    try:\n        while isinstance(name, str) and delimiter in name:\n            name, rest = name.split(delimiter, 1)\n            self, name = self[name], rest  # pylint: disable=W0642\n    except (AttributeError, TypeError):\n        raise KeyError(name) from None\n    # if value is a python dict\n    if not isinstance(self, NestedDict):\n        del self[name]\n        return\n    super().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\n    self, other: Mapping | Iterable | PathStr, recursive: bool = True\n) -> Self:\n    r\"\"\"\n    Difference between `NestedDict` and `other`.\n\n    Args:\n        other (Mapping | Iterable | PathStr):\n        recursive (bool):\n\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    \"\"\"\n\n    if isinstance(other, (PathLike, str, bytes)):\n        other = self.load(other)\n    if isinstance(other, (Mapping,)):\n        other = self.empty(other).items()\n    if not isinstance(other, Iterable):\n        raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n    return self.empty(self._difference(self, other, recursive))\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:\n    r\"\"\"\n    Get value from `NestedDict`.\n\n    Note that `default` has higher priority than `default_factory`.\n\n    Args:\n        name:\n        default:\n\n    Returns:\n        value:\n            If `NestedDict` does not contain `name`, return `default`.\n            If `default` is not specified, return `default_factory()`.\n\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\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    \"\"\"\n\n    delimiter = self.getattr(\"delimiter\", \".\")\n    if fallback is None:\n        fallback = self.getattr(\"fallback\", False)\n    fallback_name = name.split(delimiter)[-1] if isinstance(name, str) else name\n    fallback_value = Null\n    try:\n        while isinstance(name, str) and delimiter in name:\n            if fallback and fallback_name in self:\n                fallback_value = self.get(fallback_name)\n            name, rest = name.split(delimiter, 1)\n            self, name = self[name], rest  # pylint: disable=W0642\n    except (KeyError, AttributeError, TypeError):\n        if fallback and fallback_value is not Null:\n            return fallback_value\n        if default is not Null:\n            return default\n        raise KeyError(name) from None\n    if (fallback and fallback_value is not Null) and (not isinstance(self, Iterable) or name not in self):\n        return fallback_value\n    # if value is a python dict\n    if not isinstance(self, NestedDict):\n        if name not in self and default is not Null:\n            return default\n        return self[name]\n    return 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(self, other: Mapping | Iterable | PathStr, recursive: bool = True) -> Self:  # pylint: disable=W0221\n    r\"\"\"\n    Intersection of `NestedDict` and `other`.\n\n    Args:\n        other (Mapping | Iterable | PathStr):\n        recursive (bool):\n\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    \"\"\"\n\n    if isinstance(other, (PathLike, str, bytes)):\n        other = self.load(other)\n    if isinstance(other, (Mapping,)):\n        other = self.empty(other).items()\n    if not isinstance(other, Iterable):\n        raise TypeError(f\"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.\")\n    return self.empty(self._intersect(self, other, recursive))\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:\n    r\"\"\"\n    Pop value from `NestedDict`.\n\n    Args:\n        name:\n        default:\n\n    Returns:\n        value: If `NestedDict` does not contain `name`, return `default`.\n\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    \"\"\"\n\n    delimiter = self.getattr(\"delimiter\", \".\")\n    try:\n        while isinstance(name, str) and delimiter in name:\n            name, rest = name.split(delimiter, 1)\n            self, name = self[name], rest  # pylint: disable=W0642\n    except (AttributeError, TypeError):\n        raise KeyError(name) from None\n    if not isinstance(self, dict) or name not in self:\n        if default is not Null:\n            return default\n        raise KeyError(name)\n    return 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\n    self,\n    name: Any,\n    value: Any,\n    convert_mapping: bool | None = None,\n) -> None:\n    r\"\"\"\n    Set value of `NestedDict`.\n\n    Args:\n        name:\n        value:\n        convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n            Defaults to self.convert_mapping.\n\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\n\n    full_name = name\n    delimiter = self.getattr(\"delimiter\", \".\")\n    if convert_mapping is None:\n        convert_mapping = self.getattr(\"convert_mapping\", False)\n    default_factory = self.getattr(\"default_factory\", self.empty) or self.empty\n    try:\n        while isinstance(name, str) and delimiter in name:\n            name, rest = name.split(delimiter, 1)\n            if name in dir(self) and isinstance(getattr(self.__class__, name), (property, cached_property)):\n                self, name = getattr(self, name), rest\n            elif name not in self and isinstance(self, Mapping):\n                default = (\n                    self.__missing__(name, default_factory()) if hasattr(self, \"__missing__\") else default_factory()\n                )\n                self, name = default, rest\n            else:\n                self, name = self[name], rest\n            if isinstance(self, NestedDict):\n                default_factory = self.getattr(\"default_factory\", self.empty) or self.empty\n    except (AttributeError, TypeError):\n        raise KeyError(name) from None\n\n    if (\n        convert_mapping\n        and not isinstance(value, default_factory if isinstance(default_factory, type) else type(self))\n        and not isinstance(value, Variable)\n    ):\n        if isinstance(value, Mapping):\n            try:\n                value = default_factory(**value)\n            except TypeError:\n                value = default_factory(value)\n        if isinstance(value, list):\n            value = [default_factory(v) if isinstance(v, Mapping) else v for v in value]\n        if isinstance(value, tuple):\n            value = tuple(default_factory(v) if isinstance(v, Mapping) else v for v in value)\n        if isinstance(value, set):\n            value = {default_factory(v) if isinstance(v, Mapping) else v for v in list(value)}\n    if isinstance(self, NestedDict):\n        super().set(name, value)\n    elif isinstance(self, Mapping):\n        dict.__setitem__(self, name, value)\n    else:\n        raise ValueError(\n            f\"Cannot set `{full_name}` to `{value}`, as `{delimiter.join(full_name.split(delimiter)[:-1])}={self}`.\"\n        )\n
"},{"location":"nested_dict/#chanfig.NestedDict.setdefault","title":"setdefault(name, value, convert_mapping=None)","text":"

Set default value for 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

Returns:

Name Type Description value Any

If NestedDict does not contain name, return value.

Examples:

Python Console Session
>>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\", \"n.a.b.c\": 1})\n>>> d.setdefault(\"d.i\", 1031)\n1031\n>>> d.setdefault(\"i.d\", \"chang\")\n1013\n>>> d.setdefault(\"f.n\", 1013)\n'chang'\n>>> d.setdefault(\"n.a.b.d\", 2)\n2\n
Source code in chanfig/nested_dict.py Python
def setdefault(  # type: ignore[override]  # pylint: disable=R0912,W0221\n    self,\n    name: Any,\n    value: Any,\n    convert_mapping: bool | None = None,\n) -> Any:\n    r\"\"\"\n    Set default value for `NestedDict`.\n\n    Args:\n        name:\n        value:\n        convert_mapping: Whether to convert `Mapping` to `NestedDict`.\n            Defaults to self.convert_mapping.\n\n    Returns:\n        value: If `NestedDict` does not contain `name`, return `value`.\n\n    Examples:\n        >>> d = NestedDict({\"i.d\": 1013, \"f.n\": \"chang\", \"n.a.b.c\": 1})\n        >>> d.setdefault(\"d.i\", 1031)\n        1031\n        >>> d.setdefault(\"i.d\", \"chang\")\n        1013\n        >>> d.setdefault(\"f.n\", 1013)\n        'chang'\n        >>> d.setdefault(\"n.a.b.d\", 2)\n        2\n    \"\"\"\n    # pylint: disable=W0642\n\n    full_name = name\n    delimiter = self.getattr(\"delimiter\", \".\")\n    if convert_mapping is None:\n        convert_mapping = self.getattr(\"convert_mapping\", False)\n    default_factory = self.getattr(\"default_factory\", self.empty) or self.empty\n    try:\n        while isinstance(name, str) and delimiter in name:\n            name, rest = name.split(delimiter, 1)\n            if name in dir(self) and isinstance(getattr(self.__class__, name), (property, cached_property)):\n                self, name = getattr(self, name), rest\n            elif name not in self and isinstance(self, Mapping):\n                default = (\n                    self.__missing__(name, default_factory()) if hasattr(self, \"__missing__\") else default_factory()\n                )\n                self, name = default, rest\n            else:\n                self, name = self[name], rest\n            if isinstance(self, NestedDict):\n                default_factory = self.getattr(\"default_factory\", self.empty) or self.empty\n    except (AttributeError, TypeError):\n        raise KeyError(name) from None\n\n    if isinstance(self, NestedDict) and name in self:\n        return super().get(name)\n    elif isinstance(self, Mapping) and name in self:\n        dict.__getitem__(self, name)\n\n    if (\n        convert_mapping\n        and isinstance(value, Mapping)\n        and not isinstance(value, default_factory if isinstance(default_factory, type) else type(self))\n        and not isinstance(value, Variable)\n    ):\n        try:\n            value = default_factory(**value)\n        except TypeError:\n            value = default_factory(value)\n    if isinstance(self, NestedDict):\n        super().set(name, value)\n    elif isinstance(self, Mapping):\n        dict.__setitem__(self, name, value)\n    else:\n        raise ValueError(\n            f\"Cannot set `{full_name}` to `{value}`, as `{delimiter.join(full_name.split(delimiter)[:-1])}={self}`.\"\n        )\n    return value\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]\n
Source code in chanfig/nested_dict.py Python
def sort(self, key: Callable | None = None, reverse: bool = False, recursive: bool = True) -> Self:\n    r\"\"\"\n    Sort `NestedDict`.\n\n    Args:\n        recursive (bool): Whether to apply `sort` recursively.\n\n    Returns:\n        (NestedDict):\n\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]\n    \"\"\"\n\n    if recursive:\n        for value in self.values():\n            if isinstance(value, FlatDict):\n                value.sort(key=key, reverse=reverse)\n    return super().sort(key=key, reverse=reverse)\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:\n    r\"\"\"\n    Validate `NestedDict`.\n\n    Raises:\n        TypeError: If `Variable` has invalid type.\n        ValueError: If `Variable` has invalid value.\n\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    \"\"\"\n\n    self.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 new parse_config and parse method to parse command-line arguments to CHANfiG.Config object.

parse_config will read the configuration and determine possible arguments and their types. This makes it more favourable than parse as it has strict name checking.

parse will try to parse any command-line arguments, even if they are not pre-defined by 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.

ConfigParser override parse_args method to ensure the output is a NestedDict.

Source code in chanfig/parser.py Python
class ConfigParser(ArgumentParser):  # pylint: disable=C0115\n    r\"\"\"\n    Parser to parse command-line arguments for CHANfiG.\n\n    `ConfigParser` is a subclass of `argparse.ArgumentParser`.\n    It provides new `parse_config` and `parse` method to parse command-line arguments to `CHANfiG.Config` object.\n\n    `parse_config` will read the configuration and determine possible arguments and their types.\n    This makes it more favourable than `parse` as it has strict name checking.\n\n    `parse` will try to parse any command-line arguments, even if they are not pre-defined by `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\n    `ConfigParser` override `parse_args` method to ensure the output is a `NestedDict`.\n    \"\"\"\n\n    def __init__(self, *args: Any, **kwargs: Any):\n        super().__init__(*args, **kwargs)\n        self._registries[\"action\"][None] = StoreAction\n        self._registries[\"action\"][\"store\"] = StoreAction\n\n    def parse_config(  # pylint: disable=R0912\n        self,\n        args: Sequence[str] | None = None,\n        config: Config | None = None,\n        default_config: str | None = None,\n        no_default_config_action: str = \"raise\",\n    ) -> Config:\n        r\"\"\"\n        Parse the arguments for `Config`.\n\n        You may optionally specify a name for `default_config`,\n        and CHANfiG will read the file under this name.\n\n        There are three levels of config:\n\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\n        Higher levels override lower levels (i.e. 3 > 2 > 1).\n\n        Args:\n            args (Sequence[str] | None, optional): Command-line arguments. Defaults to `None`.\n            config (NestedDict | None, optional): existing configuration.\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\n        Returns:\n            config: The parsed `Config`.\n\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\n        See Also:\n            [`parse`][chanfig.ConfigParser.parse]: Parse all command-line arguments.\n\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\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        \"\"\"\n\n        if args is None:\n            args = sys.argv[1:]\n\n        if config is None:\n            raise ValueError(\"config must be specified\")\n        self.add_config_arguments(config)\n\n        if no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\n            raise ValueError(\n                f\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n            )\n\n        # parse the command-line arguments\n        parsed = self.parse_args(args)\n\n        # parse the default config file\n        if default_config is not None:\n            parsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n\n        if config.getattr(\"parser\", None) is not self:\n            config.setattr(\"parser\", self)\n        return config.merge(parsed)\n\n    def parse(  # pylint: disable=R0912\n        self,\n        args: Sequence[str] | None = None,\n        config: Config | None = None,\n        default_config: str | None = None,\n        no_default_config_action: str = \"raise\",\n    ) -> Config:\n        r\"\"\"\n        Parse the arguments for `Config`.\n\n        You may optionally specify a name for `default_config`,\n        and CHANfiG will read the file under this name.\n\n        There are three levels of config:\n\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\n        Higher levels override lower levels (i.e. 3 > 2 > 1).\n\n        Args:\n            args (Sequence[str] | None, optional): Command-line arguments. Defaults to `None`.\n            config (NestedDict | None, optional): existing configuration.\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\n        Returns:\n            config: The parsed `Config`.\n\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\n        See Also:\n            [`parse_config`][chanfig.ConfigParser.parse_config]: Only parse valid config arguments.\n\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\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\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\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\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\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        \"\"\"\n\n        if args is None:\n            args = sys.argv[1:]\n\n        if config is None:\n            from .config import Config  # pylint: disable=C0415\n\n            config = Config()\n        else:\n            self.add_config_arguments(config)\n\n        if no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\n            raise ValueError(\n                f\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n            )\n\n        # add the command-line arguments\n        key_value_args = []\n        for arg in args:\n            if args == \"--\":\n                break\n            if arg.startswith(\"-\"):\n                key_value_args.append(arg.split(\"=\", maxsplit=1))\n            else:\n                if not key_value_args:\n                    continue\n                key_value_args[-1].append(arg)\n        for key_value in key_value_args:\n            if key_value[0] not in self:\n                if len(key_value) > 2:\n                    self.add_argument(key_value[0], nargs=\"+\")\n                else:\n                    self.add_argument(key_value[0])\n\n        # parse the command-line arguments\n        parsed = self.parse_args(args)\n\n        # parse the default config file\n        if default_config is not None:\n            parsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n\n        if config.getattr(\"parser\", None) is not self:\n            config.setattr(\"parser\", self)\n        return config.merge(parsed)\n\n    def parse_args(  # type: ignore[override]\n        self, args: Sequence[str] | None = None, namespace: NestedDict | None = None, eval_str: bool = True\n    ) -> NestedDict:\n        r\"\"\"\n        Parse command line arguments and convert types.\n\n        This function first calls `ArgumentParser.parse_args` to parse command line arguments.\n        It ensures the returned parsed values is stored in a NestedDict instance.\n        If `eval_str` is specified, it also performs `literal_eval` on all `str` values.\n\n        Args:\n            args (Sequence[str] | None, optional): Command-line arguments. Defaults to `None`.\n            namespace (NestedDict | None, optional): existing configuration.\n            eval_str (bool, optional): Whether to evaluate string values.\n        \"\"\"\n        parsed: dict | Namespace = super().parse_args(args, namespace)\n        if isinstance(parsed, Namespace):\n            parsed = vars(parsed)\n        if not isinstance(parsed, NestedDict):\n            parsed = NestedDict({key: value for key, value in parsed.items() if value is not Null})\n        if eval_str:\n            for key, value in parsed.all_items():\n                if isinstance(value, str):\n                    with suppress(TypeError, ValueError, SyntaxError):\n                        value = literal_eval(value)\n                    parsed[key] = value\n        return parsed\n\n    def add_config_arguments(self, config: Config):\n        for key, dtype in get_annotations(config).items():\n            self.add_config_argument(key, dtype=dtype)\n        for key, value in config.all_items():\n            self.add_config_argument(key, value)\n\n    def add_config_argument(self, key, value: Any | None = None, dtype: type | None = None):\n        if dtype is None:\n            if isinstance(value, Variable):\n                dtype = value._type or value.dtype  # pylint: disable=W0212\n            elif isinstance(value, Field):\n                dtype = value.type\n            elif value is not None:\n                dtype = type(value)\n        if _should_collect_from_parameters(dtype):\n            args = get_args(dtype)\n            if len(args) == 2 and NoneType in args:\n                dtype = args[0] if args[0] is not NoneType else args[1]\n        name = \"--\" + key\n        if name not in self:\n            help = None  # pylint: disable=W0622\n            if isinstance(value, Variable):\n                help = value._help  # pylint: disable=W0212\n            elif isinstance(value, Field):\n                help = value.metadata.get(\"help\")\n            if dtype is None or not isclass(dtype):\n                return self.add_argument(name, help=help, dest=key)\n            if issubclass(dtype, (list, tuple, dict, set)):\n                return self.add_argument(name, type=dtype, nargs=\"+\", help=help, dest=key)\n            if issubclass(dtype, bool):\n                return self.add_argument(name, type=parse_bool, help=help, dest=key)\n            return self.add_argument(name, type=dtype, help=help, dest=key)\n\n    def merge_default_config(self, parsed, default_config: str, no_default_config_action: str = \"raise\") -> NestedDict:\n        message = f\"default_config is set to {default_config}, but not found in args.\"\n        if default_config in parsed:\n            path = parsed[default_config]\n            warn(f\"Config has 'default_config={path}' specified, its values will override values in Config\")\n            return NestedDict.load(path).merge(parsed)\n        if no_default_config_action == \"ignore\":\n            pass\n        elif no_default_config_action == \"warn\":\n            warn(message, category=RuntimeWarning, stacklevel=2)\n        else:\n            raise RuntimeError(message)\n        return parsed\n\n    @staticmethod\n    def identity(string):\n        r\"\"\"\n        https://stackoverflow.com/questions/69896931/cant-pickle-local-object-argumentparser-init-locals-identity\n        \"\"\"\n\n        return string\n\n    def __contains__(self, name: str):\n        if name in self._option_string_actions:\n            return True\n        return False\n
"},{"location":"parser/#chanfig.ConfigParser.identity","title":"identity(string) staticmethod","text":"Source code in chanfig/parser.py Python
@staticmethod\ndef identity(string):\n    r\"\"\"\n    https://stackoverflow.com/questions/69896931/cant-pickle-local-object-argumentparser-init-locals-identity\n    \"\"\"\n\n    return 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 Sequence[str] | None

Command-line arguments. Defaults to None.

None config NestedDict | None

existing configuration.

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\n    self,\n    args: Sequence[str] | None = None,\n    config: Config | None = None,\n    default_config: str | None = None,\n    no_default_config_action: str = \"raise\",\n) -> Config:\n    r\"\"\"\n    Parse the arguments for `Config`.\n\n    You may optionally specify a name for `default_config`,\n    and CHANfiG will read the file under this name.\n\n    There are three levels of config:\n\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\n    Higher levels override lower levels (i.e. 3 > 2 > 1).\n\n    Args:\n        args (Sequence[str] | None, optional): Command-line arguments. Defaults to `None`.\n        config (NestedDict | None, optional): existing configuration.\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\n    Returns:\n        config: The parsed `Config`.\n\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\n    See Also:\n        [`parse_config`][chanfig.ConfigParser.parse_config]: Only parse valid config arguments.\n\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\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\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\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\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\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    \"\"\"\n\n    if args is None:\n        args = sys.argv[1:]\n\n    if config is None:\n        from .config import Config  # pylint: disable=C0415\n\n        config = Config()\n    else:\n        self.add_config_arguments(config)\n\n    if no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\n        raise ValueError(\n            f\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n        )\n\n    # add the command-line arguments\n    key_value_args = []\n    for arg in args:\n        if args == \"--\":\n            break\n        if arg.startswith(\"-\"):\n            key_value_args.append(arg.split(\"=\", maxsplit=1))\n        else:\n            if not key_value_args:\n                continue\n            key_value_args[-1].append(arg)\n    for key_value in key_value_args:\n        if key_value[0] not in self:\n            if len(key_value) > 2:\n                self.add_argument(key_value[0], nargs=\"+\")\n            else:\n                self.add_argument(key_value[0])\n\n    # parse the command-line arguments\n    parsed = self.parse_args(args)\n\n    # parse the default config file\n    if default_config is not None:\n        parsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n\n    if config.getattr(\"parser\", None) is not self:\n        config.setattr(\"parser\", self)\n    return config.merge(parsed)\n
"},{"location":"parser/#chanfig.ConfigParser.parse_args","title":"parse_args(args=None, namespace=None, eval_str=True)","text":"

Parse command line arguments and convert types.

This function first calls ArgumentParser.parse_args to parse command line arguments. It ensures the returned parsed values is stored in a NestedDict instance. If eval_str is specified, it also performs literal_eval on all str values.

Parameters:

Name Type Description Default args Sequence[str] | None

Command-line arguments. Defaults to None.

None namespace NestedDict | None

existing configuration.

None eval_str bool

Whether to evaluate string values.

True Source code in chanfig/parser.py Python
def parse_args(  # type: ignore[override]\n    self, args: Sequence[str] | None = None, namespace: NestedDict | None = None, eval_str: bool = True\n) -> NestedDict:\n    r\"\"\"\n    Parse command line arguments and convert types.\n\n    This function first calls `ArgumentParser.parse_args` to parse command line arguments.\n    It ensures the returned parsed values is stored in a NestedDict instance.\n    If `eval_str` is specified, it also performs `literal_eval` on all `str` values.\n\n    Args:\n        args (Sequence[str] | None, optional): Command-line arguments. Defaults to `None`.\n        namespace (NestedDict | None, optional): existing configuration.\n        eval_str (bool, optional): Whether to evaluate string values.\n    \"\"\"\n    parsed: dict | Namespace = super().parse_args(args, namespace)\n    if isinstance(parsed, Namespace):\n        parsed = vars(parsed)\n    if not isinstance(parsed, NestedDict):\n        parsed = NestedDict({key: value for key, value in parsed.items() if value is not Null})\n    if eval_str:\n        for key, value in parsed.all_items():\n            if isinstance(value, str):\n                with suppress(TypeError, ValueError, SyntaxError):\n                    value = literal_eval(value)\n                parsed[key] = value\n    return parsed\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 Sequence[str] | None

Command-line arguments. Defaults to None.

None config NestedDict | None

existing configuration.

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\n    self,\n    args: Sequence[str] | None = None,\n    config: Config | None = None,\n    default_config: str | None = None,\n    no_default_config_action: str = \"raise\",\n) -> Config:\n    r\"\"\"\n    Parse the arguments for `Config`.\n\n    You may optionally specify a name for `default_config`,\n    and CHANfiG will read the file under this name.\n\n    There are three levels of config:\n\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\n    Higher levels override lower levels (i.e. 3 > 2 > 1).\n\n    Args:\n        args (Sequence[str] | None, optional): Command-line arguments. Defaults to `None`.\n        config (NestedDict | None, optional): existing configuration.\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\n    Returns:\n        config: The parsed `Config`.\n\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\n    See Also:\n        [`parse`][chanfig.ConfigParser.parse]: Parse all command-line arguments.\n\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\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    \"\"\"\n\n    if args is None:\n        args = sys.argv[1:]\n\n    if config is None:\n        raise ValueError(\"config must be specified\")\n    self.add_config_arguments(config)\n\n    if no_default_config_action not in (\"warn\", \"ignore\", \"raise\"):\n        raise ValueError(\n            f\"no_default_config_action must be one of 'warn', 'ignore', 'raise', bug got {no_default_config_action}\"\n        )\n\n    # parse the command-line arguments\n    parsed = self.parse_args(args)\n\n    # parse the default config file\n    if default_config is not None:\n        parsed = self.merge_default_config(parsed, default_config, no_default_config_action)\n\n    if config.getattr(\"parser\", None) is not self:\n        config.setattr(\"parser\", self)\n    return config.merge(parsed)\n
"},{"location":"registry/","title":"Registry","text":""},{"location":"registry/#chanfig.registry.ConfigRegistry","title":"ConfigRegistry","text":"

Bases: Registry

ConfigRegistry for components that can be initialised with a config.

ConfigRegistry is particularly useful when you want to construct a component from a configuration, such as a Hugginface Transformers model.

See Also

Registry: General purpose Registry.

Examples:

Python Console Session
>>> from dataclasses import dataclass, field\n>>> @dataclass\n... class Config:\n...     a: int\n...     b: int\n...     mode: str = \"proj\"\n>>> registry = ConfigRegistry(key=\"mode\")\n>>> @registry.register(\"proj\")\n... class Proj:\n...     def __init__(self, config):\n...         self.a = config.a\n...         self.b = config.b\n>>> @registry.register(\"inv\")\n... class Inv:\n...     def __init__(self, config):\n...         self.a = config.b\n...         self.b = config.a\n>>> registry\nConfigRegistry(\n  ('proj'): <class 'chanfig.registry.Proj'>\n  ('inv'): <class 'chanfig.registry.Inv'>\n)\n>>> config = Config(a=0, b=1)\n>>> module = registry.build(config)\n>>> module.a, module.b\n(0, 1)\n>>> config = Config(a=0, b=1, mode=\"inv\")\n>>> module = registry.build(config)\n>>> module.a, module.b\n(1, 0)\n>>> @dataclass\n... class ModuleConfig:\n...     a: int = 0\n...     b: int = 1\n...     mode: str = \"proj\"\n>>> @dataclass\n... class NestedConfig:\n...     module: ModuleConfig = field(default_factory=ModuleConfig)\n>>> nested_registry = ConfigRegistry(key=\"module.mode\")\n>>> @nested_registry.register(\"proj\")\n... class Proj:\n...     def __init__(self, config):\n...         self.a = config.module.a\n...         self.b = config.module.b\n>>> @nested_registry.register(\"inv\")\n... class Inv:\n...     def __init__(self, config):\n...         self.a = config.module.b\n...         self.b = config.module.a\n>>> nested_config = NestedConfig()\n>>> module = nested_registry.build(nested_config)\n>>> module.a, module.b\n(0, 1)\n
Source code in chanfig/registry.py Python
class ConfigRegistry(Registry):\n    \"\"\"\n    `ConfigRegistry` for components that can be initialised with a `config`.\n\n    `ConfigRegistry` is particularly useful when you want to construct a component from a configuration, such as a\n    Hugginface Transformers model.\n\n    See Also:\n        [`Registry`][chanfig.Registry]: General purpose Registry.\n\n    Examples:\n        >>> from dataclasses import dataclass, field\n        >>> @dataclass\n        ... class Config:\n        ...     a: int\n        ...     b: int\n        ...     mode: str = \"proj\"\n        >>> registry = ConfigRegistry(key=\"mode\")\n        >>> @registry.register(\"proj\")\n        ... class Proj:\n        ...     def __init__(self, config):\n        ...         self.a = config.a\n        ...         self.b = config.b\n        >>> @registry.register(\"inv\")\n        ... class Inv:\n        ...     def __init__(self, config):\n        ...         self.a = config.b\n        ...         self.b = config.a\n        >>> registry\n        ConfigRegistry(\n          ('proj'): <class 'chanfig.registry.Proj'>\n          ('inv'): <class 'chanfig.registry.Inv'>\n        )\n        >>> config = Config(a=0, b=1)\n        >>> module = registry.build(config)\n        >>> module.a, module.b\n        (0, 1)\n        >>> config = Config(a=0, b=1, mode=\"inv\")\n        >>> module = registry.build(config)\n        >>> module.a, module.b\n        (1, 0)\n        >>> @dataclass\n        ... class ModuleConfig:\n        ...     a: int = 0\n        ...     b: int = 1\n        ...     mode: str = \"proj\"\n        >>> @dataclass\n        ... class NestedConfig:\n        ...     module: ModuleConfig = field(default_factory=ModuleConfig)\n        >>> nested_registry = ConfigRegistry(key=\"module.mode\")\n        >>> @nested_registry.register(\"proj\")\n        ... class Proj:\n        ...     def __init__(self, config):\n        ...         self.a = config.module.a\n        ...         self.b = config.module.b\n        >>> @nested_registry.register(\"inv\")\n        ... class Inv:\n        ...     def __init__(self, config):\n        ...         self.a = config.module.b\n        ...         self.b = config.module.a\n        >>> nested_config = NestedConfig()\n        >>> module = nested_registry.build(nested_config)\n        >>> module.a, module.b\n        (0, 1)\n    \"\"\"\n\n    @staticmethod\n    def init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211\n        r\"\"\"\n        Constructor of component.\n\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\n        Returns:\n            (Any):\n\n        Examples:\n            >>> class Module:\n            ...     def __init__(self, config, a=None, b=None):\n            ...         self.config = config\n            ...         self.a = config.a if a is None else a\n            ...         self.b = config.b if b is None else b\n            >>> config = NestedDict({\"a\": 0, \"b\": 1})\n            >>> module = ConfigRegistry.init(Module, config, b=2)\n            >>> module.a, module.b\n            (0, 2)\n        \"\"\"\n\n        return cls(config, *args, **kwargs)\n\n    def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]\n        r\"\"\"\n        Build a component.\n\n        Args:\n            config\n\n        Returns:\n            (Any):\n\n        Raises:\n            KeyError: If the component is not registered.\n\n        Examples:\n            >>> from dataclasses import dataclass, field\n            >>> registry = ConfigRegistry(key=\"module.mode\")\n            >>> @registry.register(\"proj\")\n            ... class Proj:\n            ...     def __init__(self, config):\n            ...         self.a = config.module.a\n            ...         self.b = config.module.b\n            >>> @registry.register(\"inv\")\n            ... class Inv:\n            ...     def __init__(self, config):\n            ...         self.a = config.module.b\n            ...         self.b = config.module.a\n            >>> @dataclass\n            ... class ModuleConfig:\n            ...     a: int = 0\n            ...     b: int = 1\n            ...     mode: str = \"proj\"\n            >>> @dataclass\n            ... class Config:\n            ...     module: ModuleConfig = field(default_factory=ModuleConfig)\n            >>> config = Config()\n            >>> module = registry.build(config)\n            >>> type(module)\n            <class 'chanfig.registry.Proj'>\n            >>> module.a, module.b\n            (0, 1)\n            >>> type(module)\n            <class 'chanfig.registry.Proj'>\n        \"\"\"\n\n        key = self.getattr(\"key\")\n        config_ = deepcopy(config)\n\n        while \".\" in key:\n            key, rest = key.split(\".\", 1)\n            config_, key = getattr(config_, key), rest\n        name = getattr(config_, key)\n\n        return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]\n
"},{"location":"registry/#chanfig.registry.ConfigRegistry.build","title":"build(config, *args, **kwargs)","text":"

Build a component.

Returns:

Type Description Any

Raises:

Type Description KeyError

If the component is not registered.

Examples:

Python Console Session
>>> from dataclasses import dataclass, field\n>>> registry = ConfigRegistry(key=\"module.mode\")\n>>> @registry.register(\"proj\")\n... class Proj:\n...     def __init__(self, config):\n...         self.a = config.module.a\n...         self.b = config.module.b\n>>> @registry.register(\"inv\")\n... class Inv:\n...     def __init__(self, config):\n...         self.a = config.module.b\n...         self.b = config.module.a\n>>> @dataclass\n... class ModuleConfig:\n...     a: int = 0\n...     b: int = 1\n...     mode: str = \"proj\"\n>>> @dataclass\n... class Config:\n...     module: ModuleConfig = field(default_factory=ModuleConfig)\n>>> config = Config()\n>>> module = registry.build(config)\n>>> type(module)\n<class 'chanfig.registry.Proj'>\n>>> module.a, module.b\n(0, 1)\n>>> type(module)\n<class 'chanfig.registry.Proj'>\n
Source code in chanfig/registry.py Python
def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]\n    r\"\"\"\n    Build a component.\n\n    Args:\n        config\n\n    Returns:\n        (Any):\n\n    Raises:\n        KeyError: If the component is not registered.\n\n    Examples:\n        >>> from dataclasses import dataclass, field\n        >>> registry = ConfigRegistry(key=\"module.mode\")\n        >>> @registry.register(\"proj\")\n        ... class Proj:\n        ...     def __init__(self, config):\n        ...         self.a = config.module.a\n        ...         self.b = config.module.b\n        >>> @registry.register(\"inv\")\n        ... class Inv:\n        ...     def __init__(self, config):\n        ...         self.a = config.module.b\n        ...         self.b = config.module.a\n        >>> @dataclass\n        ... class ModuleConfig:\n        ...     a: int = 0\n        ...     b: int = 1\n        ...     mode: str = \"proj\"\n        >>> @dataclass\n        ... class Config:\n        ...     module: ModuleConfig = field(default_factory=ModuleConfig)\n        >>> config = Config()\n        >>> module = registry.build(config)\n        >>> type(module)\n        <class 'chanfig.registry.Proj'>\n        >>> module.a, module.b\n        (0, 1)\n        >>> type(module)\n        <class 'chanfig.registry.Proj'>\n    \"\"\"\n\n    key = self.getattr(\"key\")\n    config_ = deepcopy(config)\n\n    while \".\" in key:\n        key, rest = key.split(\".\", 1)\n        config_, key = getattr(config_, key), rest\n    name = getattr(config_, key)\n\n    return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]\n
"},{"location":"registry/#chanfig.registry.ConfigRegistry.init","title":"init(config, *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, config, a=None, b=None):\n...         self.config = config\n...         self.a = config.a if a is None else a\n...         self.b = config.b if b is None else b\n>>> config = NestedDict({\"a\": 0, \"b\": 1})\n>>> module = ConfigRegistry.init(Module, config, b=2)\n>>> module.a, module.b\n(0, 2)\n
Source code in chanfig/registry.py Python
@staticmethod\ndef init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211\n    r\"\"\"\n    Constructor of component.\n\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\n    Returns:\n        (Any):\n\n    Examples:\n        >>> class Module:\n        ...     def __init__(self, config, a=None, b=None):\n        ...         self.config = config\n        ...         self.a = config.a if a is None else a\n        ...         self.b = config.b if b is None else b\n        >>> config = NestedDict({\"a\": 0, \"b\": 1})\n        >>> module = ConfigRegistry.init(Module, config, b=2)\n        >>> module.a, module.b\n        (0, 2)\n    \"\"\"\n\n    return cls(config, *args, **kwargs)\n
"},{"location":"registry/#chanfig.registry.Registry","title":"Registry","text":"

Bases: NestedDict

Registry for components.

Registry provides 3 core functionalities:

  • Register a new component.
  • Lookup for a component.
  • Build a component.

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().

See Also

ConfigRegistry: Optimised for components that can be initialised with a config.

Examples:

Python Console Session
>>> registry = Registry()\n>>> @registry.register\n... @registry.register(\"Module1\", default=True)\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\": 0, \"b\": 1}}\n>>> # registry.register(Module)\n>>> module = registry.build(config[\"module\"])\n>>> type(module)\n<class 'chanfig.registry.Module'>\n>>> module.a, module.b\n(0, 1)\n>>> config = {\"module\": {\"name\": \"NE\", \"a\": 1, \"b\": 0}}\n>>> module = registry.build(config[\"module\"])\n>>> module.a, module.b\n(1, 0)\n
Source code in chanfig/registry.py Python
class Registry(NestedDict):\n    \"\"\"\n    `Registry` for components.\n\n    `Registry` provides 3 core functionalities:\n\n    - Register a new component.\n    - Lookup for a component.\n    - Build a component.\n\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\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\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\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\n    Notes:\n        `Registry` inherits from `NestedDict`.\n\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\n    See Also:\n        [`ConfigRegistry`][chanfig.ConfigRegistry]: Optimised for components that can be initialised with a `config`.\n\n    Examples:\n        >>> registry = Registry()\n        >>> @registry.register\n        ... @registry.register(\"Module1\", default=True)\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\": 0, \"b\": 1}}\n        >>> # registry.register(Module)\n        >>> module = registry.build(config[\"module\"])\n        >>> type(module)\n        <class 'chanfig.registry.Module'>\n        >>> module.a, module.b\n        (0, 1)\n        >>> config = {\"module\": {\"name\": \"NE\", \"a\": 1, \"b\": 0}}\n        >>> module = registry.build(config[\"module\"])\n        >>> module.a, module.b\n        (1, 0)\n    \"\"\"\n\n    override: bool = False\n    key: str = \"name\"\n    default: Any = Null\n\n    def __init__(\n        self, override: bool | None = None, key: str | None = None, fallback: bool | None = None, default: Any = None\n    ):\n        super().__init__(fallback=fallback)\n        if override is not None:\n            self.setattr(\"override\", override)\n        if key is not None:\n            self.setattr(\"key\", key)\n        if default is not None:\n            self.setattr(\"default\", default)\n\n    def register(\n        self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False\n    ) -> Callable:\n        r\"\"\"\n        Register a new component.\n\n        Args:\n            component: The component to register.\n            name: The name of the component.\n\n        Returns:\n            component: The registered component.\n                Registered component are expected to be `Callable`.\n\n        Raises:\n            ValueError: If the component with the same name already registered and `Registry.override=False`.\n\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        \"\"\"\n\n        if name in self and not (override or self.override):\n            raise ValueError(f\"Component with name {name} already registered.\")\n\n        # Registry.register()\n        if name is not Null:\n            self.set(name, component)\n            if default:\n                self.setattr(\"default\", component)\n            return component\n        # @Registry.register\n        if component is not Null and callable(component) and name is Null:\n            self.set(component.__name__, component)\n            if default:\n                self.setattr(\"default\", component)\n            return component\n\n        # @Registry.register()\n        def decorator(name: Any = Null):\n            @wraps(self.register)\n            def wrapper(component):\n                if name is Null:\n                    self.set(component.__name__, component)\n                else:\n                    self.set(name, component)\n                if default:\n                    self.setattr(\"default\", component)\n                return component\n\n            return wrapper\n\n        return decorator(component)\n\n    def lookup(self, name: str, default: Any = Null) -> Any:\n        r\"\"\"\n        Lookup for a component.\n\n        Args:\n            name:\n\n        Returns:\n            (Any): The component.\n\n        Raises:\n            KeyError: If the component is not registered.\n\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        \"\"\"\n\n        if default is Null:\n            default = self.getattr(\"default\", Null)\n        return self.get(name, default)\n\n    @staticmethod\n    def init(cls: Callable, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211\n        r\"\"\"\n        Constructor of component.\n\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\n        Returns:\n            (Any):\n\n        Examples:\n            >>> class Module:\n            ...     def __init__(self, a, b):\n            ...         self.a = a\n            ...         self.b = b\n            >>> kwargs = {\"a\": 0, \"b\": 1}\n            >>> module = Registry.init(Module, **kwargs)\n            >>> type(module)\n            <class 'chanfig.registry.Module'>\n            >>> module.a, module.b\n            (0, 1)\n        \"\"\"\n\n        return cls(*args, **kwargs)\n\n    def build(self, name: str | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any:\n        r\"\"\"\n        Build a component.\n\n        Args:\n            name (str | MutableMapping):\n                If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`.\n                Note that values in `kwargs` will override values in `name` if its a `MutableMapping`.\n            *args: The arguments to pass to the component.\n            **kwargs: The keyword arguments to pass to the component.\n\n        Returns:\n            (Any):\n\n        Raises:\n            KeyError: If the component is not registered.\n\n        Examples:\n            >>> registry = Registry(key=\"model\")\n            >>> @registry.register\n            ... class Module:\n            ...     def __init__(self, a, b):\n            ...         self.a = a\n            ...         self.b = b\n            >>> config = {\"module\": {\"model\": \"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        \"\"\"\n\n        if isinstance(name, MutableMapping):\n            name = deepcopy(name)\n            name, kwargs = name.pop(self.getattr(\"key\", \"name\")), dict(name, **kwargs)  # type: ignore[arg-type]\n        if name is Null:\n            name, kwargs = kwargs.pop(self.getattr(\"key\"), None), dict(**kwargs)\n        return self.init(self.lookup(name), *args, **kwargs)  # type: ignore[arg-type]\n
"},{"location":"registry/#chanfig.registry.Registry.build","title":"build(name=Null, *args, **kwargs)","text":"

Build a component.

Parameters:

Name Type Description Default name str | MutableMapping

If its a MutableMapping, it must contain key as a member, the rest will be treated as **kwargs. Note that values in kwargs will override values in name if its a MutableMapping.

Null *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(key=\"model\")\n>>> @registry.register\n... class Module:\n...     def __init__(self, a, b):\n...         self.a = a\n...         self.b = b\n>>> config = {\"module\": {\"model\": \"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 | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any:\n    r\"\"\"\n    Build a component.\n\n    Args:\n        name (str | MutableMapping):\n            If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`.\n            Note that values in `kwargs` will override values in `name` if its a `MutableMapping`.\n        *args: The arguments to pass to the component.\n        **kwargs: The keyword arguments to pass to the component.\n\n    Returns:\n        (Any):\n\n    Raises:\n        KeyError: If the component is not registered.\n\n    Examples:\n        >>> registry = Registry(key=\"model\")\n        >>> @registry.register\n        ... class Module:\n        ...     def __init__(self, a, b):\n        ...         self.a = a\n        ...         self.b = b\n        >>> config = {\"module\": {\"model\": \"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    \"\"\"\n\n    if isinstance(name, MutableMapping):\n        name = deepcopy(name)\n        name, kwargs = name.pop(self.getattr(\"key\", \"name\")), dict(name, **kwargs)  # type: ignore[arg-type]\n    if name is Null:\n        name, kwargs = kwargs.pop(self.getattr(\"key\"), None), dict(**kwargs)\n    return self.init(self.lookup(name), *args, **kwargs)  # type: ignore[arg-type]\n
"},{"location":"registry/#chanfig.registry.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\": 0, \"b\": 1}\n>>> module = Registry.init(Module, **kwargs)\n>>> type(module)\n<class 'chanfig.registry.Module'>\n>>> module.a, module.b\n(0, 1)\n
Source code in chanfig/registry.py Python
@staticmethod\ndef init(cls: Callable, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211\n    r\"\"\"\n    Constructor of component.\n\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\n    Returns:\n        (Any):\n\n    Examples:\n        >>> class Module:\n        ...     def __init__(self, a, b):\n        ...         self.a = a\n        ...         self.b = b\n        >>> kwargs = {\"a\": 0, \"b\": 1}\n        >>> module = Registry.init(Module, **kwargs)\n        >>> type(module)\n        <class 'chanfig.registry.Module'>\n        >>> module.a, module.b\n        (0, 1)\n    \"\"\"\n\n    return cls(*args, **kwargs)\n
"},{"location":"registry/#chanfig.registry.Registry.lookup","title":"lookup(name, default=Null)","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, default: Any = Null) -> Any:\n    r\"\"\"\n    Lookup for a component.\n\n    Args:\n        name:\n\n    Returns:\n        (Any): The component.\n\n    Raises:\n        KeyError: If the component is not registered.\n\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    \"\"\"\n\n    if default is Null:\n        default = self.getattr(\"default\", Null)\n    return self.get(name, default)\n
"},{"location":"registry/#chanfig.registry.Registry.register","title":"register(component=Null, name=Null, override=False, default=False)","text":"

Register a new component.

Parameters:

Name Type Description Default component Any

The component to register.

Null name Any

The name of the component.

Null

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(\n    self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False\n) -> Callable:\n    r\"\"\"\n    Register a new component.\n\n    Args:\n        component: The component to register.\n        name: The name of the component.\n\n    Returns:\n        component: The registered component.\n            Registered component are expected to be `Callable`.\n\n    Raises:\n        ValueError: If the component with the same name already registered and `Registry.override=False`.\n\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    \"\"\"\n\n    if name in self and not (override or self.override):\n        raise ValueError(f\"Component with name {name} already registered.\")\n\n    # Registry.register()\n    if name is not Null:\n        self.set(name, component)\n        if default:\n            self.setattr(\"default\", component)\n        return component\n    # @Registry.register\n    if component is not Null and callable(component) and name is Null:\n        self.set(component.__name__, component)\n        if default:\n            self.setattr(\"default\", component)\n        return component\n\n    # @Registry.register()\n    def decorator(name: Any = Null):\n        @wraps(self.register)\n        def wrapper(component):\n            if name is Null:\n                self.set(component.__name__, component)\n            else:\n                self.set(name, component)\n            if default:\n                self.setattr(\"default\", component)\n            return component\n\n        return wrapper\n\n    return decorator(component)\n
"},{"location":"utils/","title":"Utilities","text":"

Bases: type

Metaclass for Singleton Classes.

Source code in chanfig/utils.py Python
class Singleton(type):\n    r\"\"\"\n    Metaclass for Singleton Classes.\n    \"\"\"\n\n    __instances__: Mapping[type, object] = {}\n\n    def __call__(cls, *args: Any, **kwargs: Any):\n        if cls not in cls.__instances__:\n            cls.__instances__[cls] = super().__call__(*args, **kwargs)  # type: ignore[index]\n        return 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):\n    r\"\"\"\n    NULL class.\n\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\n    `Null` is an instance of `NULL` and is recommended to be used as `obj is Null`.\n    \"\"\"\n\n    def __repr__(self):\n        return \"Null\"\n\n    def __nonzero__(self):\n        return False\n\n    def __len__(self):\n        return 0\n\n    def __call__(self, *args: Any, **kwargs: Any):\n        return self\n\n    def __contains__(self, name):\n        return False\n\n    def __iter__(self):\n        return self\n\n    def __next__(self):\n        raise StopIteration\n\n    def __getattr__(self, name):\n        return self\n\n    def __getitem__(self, index):\n        return self\n

options: heading_level: 0

Bases: JSONEncoder

JSON encoder for Config.

Source code in chanfig/utils.py Python
class JsonEncoder(JSONEncoder):\n    r\"\"\"\n    JSON encoder for Config.\n    \"\"\"\n\n    def default(self, o: Any) -> Any:\n        if hasattr(o, \"__json__\"):\n            return o.__json__()\n        return 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\n    r\"\"\"\n    YAML Dumper for Config.\n    \"\"\"\n\n    def increase_indent(self, flow: bool = False, indentless: bool = False):  # pylint: disable=W0235\n        return 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\n    r\"\"\"\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\n    r\"\"\"\n    Mutable wrapper for immutable objects.\n\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\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\n    Attributes:\n        value: The wrapped value.\n        dtype: The type of the wrapped value.\n\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\n        To temporarily disable this behaviour, you can call context manager `with Variable.unwrapped()`.\n\n        To permanently disable this behaviour, you can call `Variable.unwrap()`.\n\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    \"\"\"\n\n    wrap_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\n\n    def __init__(  # pylint: disable=R0913\n        self,\n        value: Any = Null,\n        type: type | None = None,  # pylint: disable=W0622\n        choices: list | None = None,\n        validator: Callable | None = None,\n        required: bool = False,\n        help: str | None = None,  # pylint: disable=W0622\n    ) -> None:\n        self._storage = [value]\n        self._type = type\n        self._choices = choices\n        self._validator = validator\n        self._required = required\n        self._help = help\n\n    @property  # type: ignore[misc]\n    def __class__(self) -> type:\n        return self.value.__class__ if self.wrap_type else type(self)\n\n    @property\n    def value(self) -> Any:\n        r\"\"\"\n        Fetch the object wrapped in `Variable`.\n        \"\"\"\n\n        return self._storage[0]\n\n    @value.setter\n    def value(self, value) -> None:\n        r\"\"\"\n        Assign value to the object wrapped in `Variable`.\n        \"\"\"\n\n        self.validate(value)\n        self._storage[0] = self._get_value(value)\n\n    @property\n    def dtype(self) -> type:\n        r\"\"\"\n        Data type of the object wrapped in `Variable`.\n\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        \"\"\"\n\n        return self.value.__class__\n\n    @property\n    def storage(self) -> list[Any]:\n        r\"\"\"\n        Storage of `Variable`.\n        \"\"\"\n\n        return self._storage\n\n    @property\n    def type(self) -> type | None:\n        return self._type\n\n    @property\n    def choices(self) -> list | None:\n        return self._choices\n\n    @property\n    def validator(self) -> Callable | None:\n        return self._validator\n\n    @property\n    def required(self) -> bool:\n        return self._required\n\n    @property\n    def help(self) -> str:\n        return self._help or \"\"\n\n    def validate(self, *args) -> None:\n        r\"\"\"\n        Validate if the value is valid.\n        \"\"\"\n\n        if len(args) == 0:\n            value = self.value\n        elif len(args) == 1:\n            value = args[0]\n        else:\n            raise ValueError(\"Too many arguments.\")\n        if self._required and value is Null:\n            raise RuntimeError(\"Value is required.\")\n        if self._type is not None and not isinstance(value, self._type):\n            raise TypeError(f\"Value {value} is not of type {self._type}.\")\n        if self._choices is not None and value not in self._choices:\n            raise ValueError(f\"Value {value} is not in choices {self._choices}.\")\n        if self._validator is not None and not self._validator(value):\n            raise ValueError(f\"Value {value} is not valid.\")\n\n    def get(self) -> Any:\n        r\"\"\"\n        Fetch the object wrapped in `Variable`.\n        \"\"\"\n\n        return self.value\n\n    def set(self, value) -> None:\n        r\"\"\"\n        Assign value to the object wrapped in `Variable`.\n\n        `Variable.set` is extremely useful when you want to change the value without changing the reference.\n\n        In `FlatDict.set`, all assignments of `Variable` calls `Variable.set` Internally.\n        \"\"\"\n\n        self.value = value\n\n    def __get__(self, obj, objtype=None):\n        return self\n\n    def __set__(self, obj, value):\n        self.value = value\n\n    def to(self, cls: Callable) -> Any:  # pylint: disable=C0103\n        r\"\"\"\n        Convert the object wrapped in `Variable` to target `cls`.\n\n        Args:\n            cls: The type to convert to.\n\n        Examples:\n            >>> id = Variable(1013)\n            >>> id.to(float)\n            1013.0\n            >>> id.to(str)\n            '1013.0'\n        \"\"\"\n\n        self.value = cls(self.value)\n        return self\n\n    def int(self) -> int:\n        r\"\"\"\n        Convert the object wrapped in `Variable` to python `int`.\n\n        Examples:\n            >>> id = Variable(1013.0)\n            >>> id.int()\n            1013\n        \"\"\"\n\n        return self.to(int)\n\n    def float(self) -> float:\n        r\"\"\"\n        Convert the object wrapped in `Variable` to python `float`.\n\n        Examples:\n            >>> id = Variable(1013)\n            >>> id.float()\n            1013.0\n        \"\"\"\n\n        return self.to(float)\n\n    def str(self) -> str:\n        r\"\"\"\n        Convert the object wrapped in `Variable` to python `float`.\n\n        Examples:\n            >>> id = Variable(1013)\n            >>> id.str()\n            '1013'\n        \"\"\"\n\n        return self.to(str)\n\n    def wrap(self) -> None:\n        r\"\"\"\n        Wrap the type of `Variable`.\n\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        \"\"\"\n\n        self.wrap_type = True\n\n    def unwrap(self) -> None:\n        r\"\"\"\n        Unwrap the type of `Variable`.\n\n        Examples:\n            >>> id = Variable(1013)\n            >>> id.unwrap()\n            >>> isinstance(id, int)\n            False\n        \"\"\"\n\n        self.wrap_type = False\n\n    @contextmanager\n    def unwrapped(self):\n        r\"\"\"\n        Context manager which temporarily unwrap the `Variable`.\n\n        Examples:\n            >>> id = Variable(1013)\n            >>> isinstance(id, int)\n            True\n            >>> with id.unwrapped():\n            ...    isinstance(id, int)\n            False\n        \"\"\"\n\n        wrap_type = self.wrap_type\n        self.wrap_type = False\n        try:\n            yield self\n        finally:\n            self.wrap_type = wrap_type\n\n    @staticmethod\n    def _get_value(obj) -> Any:\n        if isinstance(obj, Variable):\n            return obj.value\n        return obj\n\n    def __getattr__(self, attr) -> Any:\n        return getattr(self.value, attr)\n\n    def __lt__(self, other) -> bool:\n        return self.value < self._get_value(other)\n\n    def __le__(self, other) -> bool:\n        return self.value <= self._get_value(other)\n\n    def __eq__(self, other) -> bool:\n        return self.value == self._get_value(other)\n\n    def __ne__(self, other) -> bool:\n        return self.value != self._get_value(other)\n\n    def __ge__(self, other) -> bool:\n        return self.value >= self._get_value(other)\n\n    def __gt__(self, other) -> bool:\n        return self.value > self._get_value(other)\n\n    # def __index__(self):\n    #     return self.value.__index__()\n\n    def __invert__(self):\n        return ~self.value\n\n    def __abs__(self):\n        return abs(self.value)\n\n    def __add__(self, other):\n        return Variable(self.value + self._get_value(other))\n\n    def __radd__(self, other):\n        return Variable(self._get_value(other) + self.value)\n\n    def __iadd__(self, other):\n        self.value += self._get_value(other)\n        return self\n\n    def __and__(self, other):\n        return Variable(self.value & self._get_value(other))\n\n    def __rand__(self, other):\n        return Variable(self._get_value(other) & self.value)\n\n    def __iand__(self, other):\n        self.value &= self._get_value(other)\n        return self\n\n    def __floordiv__(self, other):\n        return Variable(self.value // self._get_value(other))\n\n    def __rfloordiv__(self, other):\n        return Variable(self._get_value(other) // self.value)\n\n    def __ifloordiv__(self, other):\n        self.value //= self._get_value(other)\n        return self\n\n    def __mod__(self, other):\n        return Variable(self.value % self._get_value(other))\n\n    def __rmod__(self, other):\n        return Variable(self._get_value(other) % self.value)\n\n    def __imod__(self, other):\n        self.value %= self._get_value(other)\n        return self\n\n    def __mul__(self, other):\n        return Variable(self.value * self._get_value(other))\n\n    def __rmul__(self, other):\n        return Variable(self._get_value(other) * self.value)\n\n    def __imul__(self, other):\n        self.value *= self._get_value(other)\n        return self\n\n    def __matmul__(self, other):\n        return Variable(self.value @ self._get_value(other))\n\n    def __rmatmul__(self, other):\n        return Variable(self._get_value(other) @ self.value)\n\n    def __imatmul__(self, other):\n        self.value @= self._get_value(other)\n        return self\n\n    def __pow__(self, other):\n        return Variable(self.value ** self._get_value(other))\n\n    def __rpow__(self, other):\n        return Variable(self._get_value(other) ** self.value)\n\n    def __ipow__(self, other):\n        self.value **= self._get_value(other)\n        return self\n\n    def __truediv__(self, other):\n        return Variable(self.value / self._get_value(other))\n\n    def __rtruediv__(self, other):\n        return Variable(self._get_value(other) / self.value)\n\n    def __itruediv__(self, other):\n        self.value /= self._get_value(other)\n        return self\n\n    def __sub__(self, other):\n        return Variable(self.value - self._get_value(other))\n\n    def __rsub__(self, other):\n        return Variable(self._get_value(other) - self.value)\n\n    def __isub__(self, other):\n        self.value -= self._get_value(other)\n        return self\n\n    def __copy__(self):\n        return Variable(self.value)\n\n    def __deepcopy__(self, memo: Mapping | None = None):\n        return Variable(copy(self.value))\n\n    def __format__(self, format_spec):\n        return self.value if isinstance(self, str) else format(self.value, format_spec)\n\n    def __iter__(self):\n        return iter(self.value)\n\n    def __next__(self):\n        return next(self.value)\n\n    def __hash__(self):\n        return hash(self.value)\n\n    def __repr__(self):\n        return repr(self.value)\n\n    def __str__(self):\n        return self.value if isinstance(self, str) else str(self.value)\n\n    def __json__(self):\n        return self.value\n\n    def __contains__(self, name):\n        return 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:\n    r\"\"\"\n    Convert the object wrapped in `Variable` to python `float`.\n\n    Examples:\n        >>> id = Variable(1013)\n        >>> id.float()\n        1013.0\n    \"\"\"\n\n    return 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:\n    r\"\"\"\n    Fetch the object wrapped in `Variable`.\n    \"\"\"\n\n    return 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:\n    r\"\"\"\n    Convert the object wrapped in `Variable` to python `int`.\n\n    Examples:\n        >>> id = Variable(1013.0)\n        >>> id.int()\n        1013\n    \"\"\"\n\n    return 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:\n    r\"\"\"\n    Assign value to the object wrapped in `Variable`.\n\n    `Variable.set` is extremely useful when you want to change the value without changing the reference.\n\n    In `FlatDict.set`, all assignments of `Variable` calls `Variable.set` Internally.\n    \"\"\"\n\n    self.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:\n    r\"\"\"\n    Convert the object wrapped in `Variable` to python `float`.\n\n    Examples:\n        >>> id = Variable(1013)\n        >>> id.str()\n        '1013'\n    \"\"\"\n\n    return 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\n    r\"\"\"\n    Convert the object wrapped in `Variable` to target `cls`.\n\n    Args:\n        cls: The type to convert to.\n\n    Examples:\n        >>> id = Variable(1013)\n        >>> id.to(float)\n        1013.0\n        >>> id.to(str)\n        '1013.0'\n    \"\"\"\n\n    self.value = cls(self.value)\n    return 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:\n    r\"\"\"\n    Unwrap the type of `Variable`.\n\n    Examples:\n        >>> id = Variable(1013)\n        >>> id.unwrap()\n        >>> isinstance(id, int)\n        False\n    \"\"\"\n\n    self.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):\n    r\"\"\"\n    Context manager which temporarily unwrap the `Variable`.\n\n    Examples:\n        >>> id = Variable(1013)\n        >>> isinstance(id, int)\n        True\n        >>> with id.unwrapped():\n        ...    isinstance(id, int)\n        False\n    \"\"\"\n\n    wrap_type = self.wrap_type\n    self.wrap_type = False\n    try:\n        yield self\n    finally:\n        self.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:\n    r\"\"\"\n    Validate if the value is valid.\n    \"\"\"\n\n    if len(args) == 0:\n        value = self.value\n    elif len(args) == 1:\n        value = args[0]\n    else:\n        raise ValueError(\"Too many arguments.\")\n    if self._required and value is Null:\n        raise RuntimeError(\"Value is required.\")\n    if self._type is not None and not isinstance(value, self._type):\n        raise TypeError(f\"Value {value} is not of type {self._type}.\")\n    if self._choices is not None and value not in self._choices:\n        raise ValueError(f\"Value {value} is not in choices {self._choices}.\")\n    if self._validator is not None and not self._validator(value):\n        raise 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:\n    r\"\"\"\n    Wrap the type of `Variable`.\n\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    \"\"\"\n\n    self.wrap_type = True\n
"},{"location":"about/","title":"About","text":"

Developed by DanLing on Earth

We are a community of developers, designers, and others from around the world who are working together to make deep learning more accessible.

We are a community of individuals who seek to push the boundaries of what is possible with deep learning.

We are passionate about Deep Learning and the people who use it.

We are DanLing.

"},{"location":"about/license/","title":"GNU AFFERO GENERAL PUBLIC LICENSE","text":"

Version 3, 19 November 2007

Copyright (C) 2007 Free Software Foundation, Inc. https://fsf.org/

Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

"},{"location":"about/license/#preamble","title":"Preamble","text":"

The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software.

The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program\u2013to make sure it remains free software for all its users.

When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things.

Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software.

A secondary benefit of defending all users\u2019 freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public.

The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version.

An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license.

The precise terms and conditions for copying, distribution and modification follow.

"},{"location":"about/license/#terms-and-conditions","title":"TERMS AND CONDITIONS","text":""},{"location":"about/license/#0-definitions","title":"0. Definitions.","text":"

\u201cThis License\u201d refers to version 3 of the GNU Affero General Public License.

\u201cCopyright\u201d also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.

\u201cThe Program\u201d refers to any copyrightable work licensed under this License. Each licensee is addressed as \u201cyou\u201d. \u201cLicensees\u201d and \u201crecipients\u201d may be individuals or organizations.

To \u201cmodify\u201d a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a \u201cmodified version\u201d of the earlier work or a work \u201cbased on\u201d the earlier work.

A \u201ccovered work\u201d means either the unmodified Program or a work based on the Program.

To \u201cpropagate\u201d a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well.

To \u201cconvey\u201d a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying.

An interactive user interface displays \u201cAppropriate Legal Notices\u201d to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion.

"},{"location":"about/license/#1-source-code","title":"1. Source Code.","text":"

The \u201csource code\u201d for a work means the preferred form of the work for making modifications to it. \u201cObject code\u201d means any non-source form of a work.

A \u201cStandard Interface\u201d means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language.

The \u201cSystem Libraries\u201d of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A \u201cMajor Component\u201d, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it.

The \u201cCorresponding Source\u201d for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work\u2019s System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work.

The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.

The Corresponding Source for a work in source code form is that same work.

"},{"location":"about/license/#2-basic-permissions","title":"2. Basic Permissions.","text":"

All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law.

You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you.

Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.

"},{"location":"about/license/#3-protecting-users-legal-rights-from-anti-circumvention-law","title":"3. Protecting Users\u2019 Legal Rights From Anti-Circumvention Law.","text":"

No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures.

When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work\u2019s users, your or third parties\u2019 legal rights to forbid circumvention of technological measures.

"},{"location":"about/license/#4-conveying-verbatim-copies","title":"4. Conveying Verbatim Copies.","text":"

You may convey verbatim copies of the Program\u2019s source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program.

You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.

"},{"location":"about/license/#5-conveying-modified-source-versions","title":"5. Conveying Modified Source Versions.","text":"

You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions:

  • a) The work must carry prominent notices stating that you modified it, and giving a relevant date.
  • b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to \u201ckeep intact all notices\u201d.
  • c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it.
  • d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so.

A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an \u201caggregate\u201d if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation\u2019s users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate.

"},{"location":"about/license/#6-conveying-non-source-forms","title":"6. Conveying Non-Source Forms.","text":"

You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways:

  • a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange.
  • b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge.
  • c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b.
  • d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements.
  • e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d.

A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work.

A \u201cUser Product\u201d is either (1) a \u201cconsumer product\u201d, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, \u201cnormally used\u201d refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product.

\u201cInstallation Information\u201d for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made.

If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM).

The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network.

Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying.

"},{"location":"about/license/#7-additional-terms","title":"7. Additional Terms.","text":"

\u201cAdditional permissions\u201d are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions.

When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission.

Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms:

  • a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or
  • b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or
  • c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or
  • d) Limiting the use for publicity purposes of names of licensors or authors of the material; or
  • e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or
  • f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors.

All other non-permissive additional terms are considered \u201cfurther restrictions\u201d within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying.

If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms.

Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way.

"},{"location":"about/license/#8-termination","title":"8. Termination.","text":"

You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11).

However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation.

Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice.

Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10.

"},{"location":"about/license/#9-acceptance-not-required-for-having-copies","title":"9. Acceptance Not Required for Having Copies.","text":"

You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so.

"},{"location":"about/license/#10-automatic-licensing-of-downstream-recipients","title":"10. Automatic Licensing of Downstream Recipients.","text":"

Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License.

An \u201centity transaction\u201d is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party\u2019s predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts.

You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it.

"},{"location":"about/license/#11-patents","title":"11. Patents.","text":"

A \u201ccontributor\u201d is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor\u2019s \u201ccontributor version\u201d.

A contributor\u2019s \u201cessential patent claims\u201d are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, \u201ccontrol\u201d includes the right to grant patent sublicenses in a manner consistent with the requirements of this License.

Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor\u2019s essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version.

In the following three paragraphs, a \u201cpatent license\u201d is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To \u201cgrant\u201d such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party.

If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. \u201cKnowingly relying\u201d means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient\u2019s use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid.

If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it.

A patent license is \u201cdiscriminatory\u201d if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007.

Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law.

"},{"location":"about/license/#12-no-surrender-of-others-freedom","title":"12. No Surrender of Others\u2019 Freedom.","text":"

If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program.

"},{"location":"about/license/#13-remote-network-interaction-use-with-the-gnu-general-public-license","title":"13. Remote Network Interaction; Use with the GNU General Public License.","text":"

Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph.

Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License.

"},{"location":"about/license/#14-revised-versions-of-this-license","title":"14. Revised Versions of this License.","text":"

The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License \u201cor any later version\u201d applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation.

If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy\u2019s public statement of acceptance of a version permanently authorizes you to choose that version for the Program.

Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version.

"},{"location":"about/license/#15-disclaimer-of-warranty","title":"15. Disclaimer of Warranty.","text":"

THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \u201cAS IS\u201d WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

"},{"location":"about/license/#16-limitation-of-liability","title":"16. Limitation of Liability.","text":"

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

"},{"location":"about/license/#17-interpretation-of-sections-15-and-16","title":"17. Interpretation of Sections 15 and 16.","text":"

If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee.

END OF TERMS AND CONDITIONS

"},{"location":"about/license/#how-to-apply-these-terms-to-your-new-programs","title":"How to Apply These Terms to Your New Programs","text":"

If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.

To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the \u201ccopyright\u201d line and a pointer to where the full notice is found.

Text Only
    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as\n    published by the Free Software Foundation, either version 3 of the\n    License, or (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n

Also add information on how to contact you by electronic and paper mail.

If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a \u201cSource\u201d link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements.

You should also get your employer (if you work as a programmer) or school, if any, to sign a \u201ccopyright disclaimer\u201d for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see https://www.gnu.org/licenses/.

"},{"location":"about/privacy/","title":"Privacy Notice","text":"

Last Revised Date

This notice was last updated on May 04, 2024.

"},{"location":"about/privacy/#privacy-notice","title":"Privacy Notice","text":"

This privacy notice for DanLing Team (also known as DanLing) (\u2018we\u2019, \u2018us\u2019, or \u2018our\u2019), describes how and why we might collect, store, use, and/or share (\u2018process\u2019) your information when you use our services (\u2018Services\u2019), such as when you:

  • Visit our website at chanfig.danling.org, or any website of ours that links to this privacy notice

You can change your privacy settings at any time by clicking the button below:

Privacy Control

Questions or concerns? Reading this privacy notice will help you understand your privacy rights and choices. If you do not agree with our policies and practices, please do not use our Services. If you still have any questions or concerns, please contact us at privacy@danling.org.

"},{"location":"about/privacy/#0-summary-of-key-points","title":"0. Summary of Key Points","text":"

This summary provides key points from our privacy notice, but you can find out more details about any of these topics by clicking the link following each key point or by using our table of contents below to find the section you are looking for.

What personal information do we process?

When you visit, use, or navigate our Services, we may process personal information depending on how you interact with us and the Services, the choices you make, and the products and features you use.

What information do we collect?

How do we process your information?

We process your information to provide, improve, and administer our Services, communicate with you, for security and fraud prevention, and to comply with law. We may also process your information for other purposes with your consent. We process your information only when we have a valid legal reason to do so.

How do we process your information?

Do we process any sensitive personal information?

We do not process any sensitive personal information.

Do we collect any information from third parties?

We do not collect any information from third parties.

In what situations and with which parties do we share personal information?

We may share information in specific situations and with specific third parties.

When and with whom we share your personal information?

How do we keep your information safe?

We have organisational and technical processes and procedures in place to protect your personal information.

How do we keep your information safe?

What are your rights?

Depending on where you are located geographically, the applicable privacy law may mean you have certain rights regarding your personal information.

What are your privacy rights?

How do you exercise your rights?

The easiest way to exercise your rights is by contacting the relevant data protection authority in your jurisdiction.

How to exercise your rights

"},{"location":"about/privacy/#1-what-information-do-we-collect","title":"1. What information do we collect?","text":""},{"location":"about/privacy/#personal-information-you-disclose-to-us","title":"Personal information you disclose to us","text":"

In Short

We collect personal information that you provide to us.

We collect personal information that you voluntarily provide to us when you express an interest in obtaining information about us or our products and Services, when you participate in activities on the Services, or otherwise when you contact us.

Sensitive Personal Information

We do not collect any sensitive personal information from you.

"},{"location":"about/privacy/#information-automatically-collected","title":"Information automatically collected","text":"

In Short

Some information \u2014 such as IP address and/or browser and device characteristics \u2014 is collected automatically when you visit our Services.

We automatically collect certain information when you visit, use, or navigate our Services. This information does not reveal your specific identity (like your name or contact information) but may include device and usage information, such as your IP address, browser and device characteristics, operating system, language preferences, referring URLs, device name, country, location, information about how and when you use our Services, and other technical information. This information is primarily needed to maintain the security and operation of our Services, and for our internal analytics and reporting purposes.

Like many businesses, we also collect information through cookies and similar technologies.

The information we collect includes:

  • Identifiers. Identifier is a device and browser-specific unique random string that we generate when you use our Service. This identifier is stored in a cookie on your device, allowing us to identify you across multiple sessions and when you return to our Service. You can delete this cookie at any time by clearing your browser\u2019s cache.
  • Log and Usage Data. Log and usage data is service-related, diagnostic, usage, and performance information our servers automatically collect when you access or use our Services and which we record in log files. Depending on how you interact with us, this log data may include your IP address, device information, browser type, and settings, and information about your activity in the Services (such as the date/time stamps associated with your usage, pages and files viewed, searches and other actions you take such as which features you use), device event information (such as system activity, error reports (sometimes called \u2018crash dumps\u2019) and hardware settings).
  • Device Data. We collect device data such as information about your computer, phone, tablet, or other devices you use to access the Services. Depending on the device used, this device data may include information such as your IP address (or proxy server), device and application identification numbers, location, browser type, hardware model, Internet Service Provider and/or mobile carrier, operating system, and system configuration information.
  • Location Data. We collect location data such as information about your device\u2019s location, which can be either precise or imprecise. How much information we collect depends on the type and settings of the device you use to access the Services. For example, we may use GPS and other technologies to collect geolocation data that tells us your current location (based on your IP address). You can opt out of allowing us to collect this information either by refusing access to the information or by disabling your location settings on your device.
"},{"location":"about/privacy/#categories-of-personal-information-we-collect","title":"Categories of Personal Information We Collect","text":"

We have collected the following categories of personal information in the past twelve (12) months:

Category Examples Collected A. Identifiers Contact details, such as real name, alias, postal address, telephone or mobile contact number, unique personal identifier, online identifier, Internet Protocol address, email address, and account name YES B. Personal information as defined in the California Customer Records statute Name, contact information, education, employment, employment history, and financial information NO C. Protected classification characteristics under state or federal law Gender, age, date of birth, race and ethnicity, national origin, marital status, and other demographic data NO D. Commercial information Transaction information, purchase history, financial details, and payment information NO E. Biometric information Fingerprints and voiceprints NO F. Internet or other similar network activity Browsing history, search history, online behaviour, interest data, and interactions with our and other websites, applications, systems, and advertisements YES G. Geolocation data Device location YES H. Audio, electronic, sensory, or similar information Images and audio, video or call recordings created in connection with our business activities NO I. Professional or employment-related information Business contact details in order to provide you our Services at a business level or job title, work history, and professional qualifications if you apply for a job with us NO J. Education Information Student records and directory information NO K. Inferences drawn from collected personal information Inferences drawn from any of the collected personal information listed above to create a profile or summary about, for example, an individual\u2019s preferences and characteristics YES L. Sensitive personal Information NO

We may also collect other personal information outside of these categories through instances where you interact with us in person, online, or by phone or mail in the context of:

  • Receiving help through our customer support channels;
  • Participation in customer surveys or contests; and
  • Facilitation in the delivery of our Services and to respond to your inquiries.

We will use and retain the collected personal information as needed to provide you with our Services and as necessary to comply with our legal obligations, resolve disputes, and enforce our agreement for the following period:

  • Category A: Indefinitely
  • Category F: Indefinitely
  • Category G: Indefinitely
  • Category K: Indefinitely
"},{"location":"about/privacy/#2-how-do-we-process-your-information","title":"2. How do we process your information?","text":"

In Short

We process your information to provide, improve, and administer our Services, communicate with you, for security and fraud prevention, and to comply with law. We may also process your information for other purposes with your consent.

We process your personal information for a variety of reasons, depending on how you interact with our Services, including:

  • To protect our Services. We may process your information as part of our efforts to keep our Services safe and secure, including fraud monitoring and prevention.
  • To identify user trends. We may process information about how you use our Services to better understand how they are being used so we can improve them.
  • To save or protect an individual\u2019s vital interest. We may process your information when necessary to save or protect an individual\u2019s vital interest, such as to prevent harm.
"},{"location":"about/privacy/#3-what-legal-basis-do-we-have-for-processing-your-information","title":"3. What legal basis do we have for processing your information?","text":"

In Short

We only process your personal information when we believe it is necessary and we have a valid legal reason (i.e. legal basis) to do so under applicable law, like with your consent, to comply with laws, to provide you with services to enter into or fulfil our contractual obligations, to protect your rights, or to fulfil our legitimate business interests.

The General Data Protection Regulation (GDPR) and UK GDPR require us to explain the valid legal bases we rely on in order to process your personal information. As such, we may rely on the following legal bases to process your personal information:

  • Consent. We may process your personal information if you have given us specific consent to use your personal information for a specific purpose. You have the right to withdraw your consent at any time. Learn more about withdrawing your consents.
  • Legitimate Interests. We may process your information when we believe it is reasonably necessary to achieve our legitimate business interests and those interests do not outweigh your interests and fundamental rights and freedoms. For example, we may process your personal information for some of the purposes described in order to:
    • Analyse how our Services are used so we can improve them to engage and retain users
    • Diagnose problems and/or prevent fraudulent activities
  • Legal Obligations. We may process your information where we believe it is necessary for compliance with our legal obligations, such as to cooperate with a law enforcement body or regulatory agency, exercise or defend our legal rights, or disclose your information as evidence in litigation in which we are involved.
  • Vital Interests. We may process your information where we believe it is necessary to protect your vital interests or the vital interests of a third party, such as situations involving potential threats to the safety of any person.

Consent to Processing in Canada

If you are located in Canada, we may be legally permitted under applicable law to process your information without your consent in some exceptional cases, including, for example:

  • If collection is clearly in the interests of an individual and consent cannot be obtained in a timely way
  • For investigations and fraud detection and prevention
  • For business transactions provided certain conditions are met
  • If it is contained in a witness statement and the collection is necessary to assess, process, or settle an insurance claim
  • For identifying injured, ill, or deceased persons and communicating with next of kin
  • If we have reasonable grounds to believe an individual has been, is, or may be victim of financial abuse
  • If it is reasonable to expect collection and use with consent would compromise the availability or the accuracy of the information and the collection is reasonable for purposes related to investigating a breach of an agreement or a contravention of the laws of Canada or a province
  • If disclosure is required to comply with a subpoena, warrant, court order, or rules of the court relating to the production of records
  • If it was produced by an individual in the course of their employment, business, or profession and the collection is consistent with the purposes for which the information was produced
  • If the collection is solely for journalistic, artistic, or literary purposes
  • If the information is publicly available and is specified by the regulations
"},{"location":"about/privacy/#4-when-and-with-whom-do-we-share-your-personal-information","title":"4. When and with whom do we share your personal information?","text":"

In Short

We may share information in specific situations described in this section and/or with the following third parties.

We may use your personal information for our business purposes, such as for undertaking internal research for technological development and demonstration. This is not considered to be \u2018selling\u2019 of your personal information.

Vendors, Consultants, and Other Third-Party Service Providers. We may share your data with third-party vendors, service providers, contractors, or agents (\u2018third parties\u2019) who perform services for us or on our behalf and require access to such information to do that work. We have contracts in place with our third parties, which are designed to help safeguard your personal information. This means that they cannot do anything with your personal information unless we have instructed them to do it. They will also not share your personal information with any organisation apart from us. They also commit to protect the data they hold on our behalf and to retain it for the period we instruct.

The third parties we may share personal information with are as follows:

  • Advertising, Direct Marketing, and Lead Generation
    • Google AdSense
  • Cloud Computing Services
    • Microsoft Azure
    • Amazon Web Services (AWS)
    • Google Cloud Platform (GCP)
  • Communications and Content Delivery Network (CDN) Services
    • Cloudflare
  • Content Optimisation
    • Google Site Search
    • Google Fonts
  • Functionality and Infrastructure Optimisation
    • GitHub Pages
  • User Commenting and Forums
    • Disqus
    • GitHub Issues
    • GitHub Discussions
  • Web and Mobile Analytics
    • Google Analytics

We also may need to share your personal information in the following situations:

  • Business Transfers. We may share or transfer your information in connection with, or during negotiations of, any merger, sale of company assets, financing, or acquisition of all or a portion of our business to another company.

We have disclosed the following categories of personal information for a business purpose in the past twelve (12) months:

Nill

The categories of third parties to whom we sold personal information in the past twelve (12) months:

Nill

The categories of third parties to whom we shared personal information with in the past twelve (12) months:

  • Web and Mobile Analytics
    • Google Analytics
"},{"location":"about/privacy/#5-do-we-use-cookies-and-other-tracking-technologies","title":"5. Do we use cookies and other tracking technologies?","text":"

In Short

We may use cookies and other tracking technologies to collect and store your information.

We also permit third parties and service providers to use online tracking technologies on our Services for analytics and advertising, including to help manage and display advertisements, to tailor advertisements to your interests, or to send abandoned shopping cart reminders (depending on your communication preferences). The third parties and service providers use their technology to provide advertising about products and services tailored to your interests which may appear either on our Services or on other websites.

To the extent these online tracking technologies are deemed to be a \u2018sale\u2019/\u2019sharing\u2019 (which includes targeted advertising, as defined under the applicable laws) under applicable US state laws, you can opt out of these online tracking technologies by clicking the button on the top of this page or the button below:

Privacy Control

"},{"location":"about/privacy/#google-analytics","title":"Google Analytics","text":"

We may share your information with Google Analytics to track and analyse the use of the Services. The Google Analytics Advertising Features that we may use include:

  • Remarketing with Google Analytics
  • Google Display Network Impressions Reporting
  • Google Analytics Demographics and Interests Reporting

To opt out of being tracked by Google Analytics across the Services, visit https://tools.google.com/dlpage/gaoptout. You can opt out of Google Analytics Advertising Features through Ads Settings and Ad Settings for mobile apps.

Other opt out means include http://optout.networkadvertising.org/ and http://www.networkadvertising.org/mobile-choice.

For more information on the privacy practices of Google, please visit the Google Privacy & Terms.

"},{"location":"about/privacy/#6-how-long-do-we-keep-your-information","title":"6. How long do we keep your information?","text":"

In Short

We keep your information for as long as necessary to fulfil the purposes outlined in this privacy notice unless otherwise required by law.

We will only keep your personal information for as long as it is necessary for the purposes set out in this privacy notice, unless a longer retention period is required or permitted by law (such as tax, accounting, or other legal requirements).

When we have no ongoing legitimate business need to process your personal information, we will either delete or anonymise it, or, if this is not possible (for example, because your personal information has been stored in backup archives), then we will securely store your personal information and isolate it from any further processing until deletion is possible.

"},{"location":"about/privacy/#7-how-do-we-keep-your-information-safe","title":"7. How do we keep your information safe?","text":"

In Short

We aim to protect your personal information through a system of organisational and technical security measures.

We have implemented appropriate technical and organisational security measures designed to protect the security of any personal information we process. However, despite our safeguards and efforts to secure your information, no electronic transmission over the internet or information storage technology can be guaranteed to be 100% secure, so we cannot promise or guarantee that hackers, cybercriminals, or other unauthorised third parties will not be able to defeat our security and improperly collect, access, steal, or modify your information. Although we will do our best to protect your personal information, the transmission of personal information to and from our Services is at your own risk. You should only access the Services within a secure environment.

"},{"location":"about/privacy/#8-what-are-your-privacy-rights","title":"8. What are your privacy rights?","text":"

In Short

We strive to protect your privacy rights and choices to the best possible extent under the law.

You have rights under certain data protection laws. However, these rights are not absolute, and in certain cases, we may decline your request as permitted by law. These rights include:

  • Right to know whether or not we are processing your personal data
  • Right to access your personal data
  • Right to correct inaccuracies in your personal data
  • Right to request the deletion of your personal data
  • Right to obtain a copy of the personal data you previously shared with us
  • Right to non-discrimination against you for exercising your rights
  • Right to opt-out
    • of the processing of your personal data if it is used for targeted advertising (or sharing as defined under applicable laws), the sale of personal data, or profiling in furtherance of decisions that produce legal or similarly significant effects (\u2018profiling\u2019) concerning you
    • of the collection of sensitive data and personal data collected through the operation of a voice or facial recognition feature
  • Right to obtain
    • a list of the categories of third parties to which we have disclosed personal data
    • a list of specific third parties to which we have disclosed personal data
  • Right to limit use and disclosure of sensitive personal data
"},{"location":"about/privacy/#how-to-exercise-your-rights","title":"How to exercise your rights","text":"

It is very unlikely that you will be able to exercise the above rights as we do not collect any identifiable personal data from you.

We are unable to reply to and act on data subject access request as we do not save any identifiable information about you, and we will not be able to verify your identity.

If you believe we are unlawfully processing your personal information, you can contact the relevant data protection regulator, state attorney general, or other competent authority in your jurisdiction.

Residency Authority European Economic Area Member State\u2019s data protection supervisory authority United Kingdom Information Commissioner\u2019s Office Australia Office of the Australian Information Commissioner New Zealand Office of New Zealand Privacy Commissioner Canada Office of the Privacy Commissioner of Canada California of the United States California Privacy Protection Agency Switzerland Federal Data Protection and Information Commissioner South Africa Information Regulator"},{"location":"about/privacy/#withdraw-your-consent","title":"Withdraw your consent","text":"

If we are relying on your consent to process your personal information, which may be express and/or implied consent depending on the applicable law, you have the right to withdraw your consent at any time. You can withdraw your consent at any time by clicking the button on the top of this page or the button below:

Privacy Control

However, please note that this will not affect the lawfulness of the processing before its withdrawal nor, when applicable law allows, will it affect the processing of your personal information conducted in reliance on lawful processing grounds other than consent.

"},{"location":"about/privacy/#cookies-and-similar-technologies","title":"Cookies and similar technologies","text":"

Most web browsers are set to accept cookies by default. If you prefer, you can usually choose to set your browser to remove or reject browser cookies. Please note that if you choose to remove or reject cookies, this will NOT affect the availability and functionality of our Services.

"},{"location":"about/privacy/#9-controls-for-do-not-track-features","title":"9. Controls for Do-Not-Track features","text":"

Most web browsers and some mobile operating systems and mobile applications include a Do-Not-Track (\u2018DNT\u2019) feature or setting you can activate to signal your privacy preference not to have data about your online browsing activities monitored and collected. At this stage, no uniform technology standard for recognising and implementing DNT signals has been finalised. Although we cannot promise to honour every DNT signal, we strive to honour all such requests where technically feasible.

California law requires us to let you know how we respond to web browser DNT signals. Because we cannot guarantee to recognise and houour all DNT signals, we do not respond to them at this time.

"},{"location":"about/privacy/#10-do-residents-in-certain-jurisdiction-have-specific-privacy-rights","title":"10. Do residents in certain jurisdiction have specific privacy rights?","text":"

NO.

All men and women are created equal.

We provide the same privacy rights to all individuals, regardless of their location.

Be assured that we will treat you with the same respect and dignity as we would want to be treated.

"},{"location":"about/privacy/#11-how-can-you-review-update-or-delete-the-data-we-collect-from-you","title":"11. How can you review, update, or delete the data we collect from you?","text":"

It is very unlikely that you will be able to review, update, or delete the data we collect from you as we do not collect any identifiable personal data from you, and we will not be able to identify which data belongs to you.

"},{"location":"about/privacy/#12-do-we-make-updates-to-this-notice","title":"12. Do we make updates to this notice?","text":"

In Short

Yes, we will update this notice as necessary to stay compliant with relevant laws.

We may update this privacy notice from time to time. The updated version will be indicated by an updated \u2018Last Revised Date\u2019 at the top of this privacy notice. If we make any material changes, we will notify you by posting the new privacy notice on this page. We are unable to notify you directly as we do not collect any contact information from you. We encourage you to review this privacy notice frequently to stay informed of how we are protecting your information.

"},{"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

ConfigRegistry\u200b\u662f\u200b\u4e00\u4e2a\u200bRegistry\u200b\u7684\u200b\u5b50\u7c7b\u200b\uff0c\u200b\u4ed6\u200b\u4e13\u4e3a\u200b\u4ece\u200b\u4e00\u4e2a\u200bConfig\u200b\u6216\u8005\u200b\u4e00\u4e2a\u200bdataclass\u200b\u6765\u200b\u6784\u5efa\u200b\u4e00\u4e2a\u200b\u5bf9\u8c61\u200b\u800c\u200b\u8bbe\u8ba1\u200b\u3002 \u200b\u53ea\u200b\u9700\u200b\u5728\u200b\u521b\u5efa\u200b\u6ce8\u518c\u8868\u200b\u65f6\u200b\u6307\u5b9a\u200bkey\uff0c\u200b\u7136\u540e\u200b\u5728\u200b\u8c03\u7528\u200bbuild\u200b\u65b9\u6cd5\u200b\u65f6\u200b\u4f20\u5165\u200bconfig\uff0c\u200b\u4f60\u200b\u5c31\u200b\u4f1a\u200b\u5f97\u5230\u200b\u4f60\u200b\u60f3\u8981\u200b\u7684\u200b\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\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

Bash
# CHANfiG, Easier Configuration.\n# Copyright (c) 2022-Present, CHANfiG Contributors\n\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the following licenses:\n# - The Unlicense\n# - GNU Affero General Public License v3.0 or later\n# - GNU General Public License v2.0 or later\n# - BSD 4-Clause \"Original\" or \"Old\" License\n# - MIT License\n# - Apache License 2.0\n\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n# See the LICENSE file for more details.\n\nimport os\n\nfrom chanfig import Config, Variable, configclass\n\n\n@configclass\nclass DataloaderConfig:\n    batch_size: int = 64\n    num_workers: int = 4\n    pin_memory: bool = True\n\n\nclass TestConfig(Config):\n    def __init__(self):\n        super().__init__()\n        dropout = Variable(0.1)\n        self.name = \"CHANfiG\"\n        self.seed = 1013\n        self.activation = \"GELU\"\n        self.optim.lr = 1e-3\n        self.dataloader = DataloaderConfig()\n        self.model.encoder.num_layers = 6\n        self.model.decoder.num_layers = 6\n        self.model.dropout = dropout\n        self.model.encoder.dropout = dropout\n        self.model.decoder.dropout = dropout\n        self.add_argument(\"--batch_size\", dest=\"data.batch_size\")\n        self.add_argument(\"--lr\", dest=\"optim.lr\")\n\n    def post(self):\n        self.id = f\"{self.name}_{self.seed}\"\n\n\nif __name__ == \"__main__\":\n    # config = Config.load('config.yaml')  # read config from a yaml\n    # config = Config.load('config.json')  # read config from a json\n    existing_config = {\"model.encoder.num_layers\": 8}\n    config = TestConfig()\n    config.merge(existing_config)\n    # config.merge('dataset.yaml')  # merge config from a yaml\n    # config.merge('dataset.json', overwrite=False)  # merge config from a json\n    config = config.parse()\n    config.model.decoder.num_layers = 8\n    config.freeze()\n    print(config)\n    # main(config)\n    dir_path = os.path.dirname(os.path.realpath(__file__))\n    print(dir_path)\n    config.save(os.path.join(dir_path, \"config.yaml\"))  # save config to a yaml\n    config.save(os.path.join(dir_path, \"config.json\"))  # save config to a 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

yamljson YAML
activation: GELU\ndataloader:\n  batch_size: 64\n  num_workers: 4\n  pin_memory: true\nid: CHANfiG_1013\nmodel:\n  decoder:\n    dropout: 0.1\n    num_layers: 8\n  dropout: 0.1\n  encoder:\n    dropout: 0.1\n    num_layers: 8\nname: CHANfiG\noptim:\n  lr: 0.001\nseed: 1013\n
JSON
{\n  \"name\": \"CHANfiG\",\n  \"seed\": 1013,\n  \"activation\": \"GELU\",\n  \"optim\": {\n    \"lr\": 0.001\n  },\n  \"dataloader\": {\n    \"batch_size\": 64,\n    \"num_workers\": 4,\n    \"pin_memory\": true\n  },\n  \"model\": {\n    \"encoder\": {\n      \"num_layers\": 8,\n      \"dropout\": 0.1\n    },\n    \"decoder\": {\n      \"num_layers\": 8,\n      \"dropout\": 0.1\n    },\n    \"dropout\": 0.1\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":"\u5b89\u88c5\u200b pypi \u200b\u4e0a\u200b\u6700\u8fd1\u200b\u7684\u200b\u7a33\u5b9a\u200b\u7248\u672c\u200b\u4ece\u200b\u6e90\u7801\u200b\u5b89\u88c5\u200b\u6700\u65b0\u200b\u7684\u200b\u7248\u672c\u200b Bash
pip install chanfig\n
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

The UnlicenseGNU Affero General Public License v3.0 or laterGNU General Public License v2.0 or laterBSD 4-Clause \u201cOriginal\u201d or \u201cOld\u201d LicenseMIT LicenseApache License 2.0 Text Only
This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <https://unlicense.org>\n
Text Only
                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<https://www.gnu.org/licenses/>.\n
Text Only
                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n
Text Only
BSD 4-Clause License\n\nCopyright (c) 2022-Present, CHANfiG Contributors\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n3. All advertising materials mentioning features or use of this software must\n   display the following acknowledgement:\n     This product includes software developed by [project].\n\n4. Neither the name of the copyright holder nor the names of its\n   contributors may be used to endorse or promote products derived from\n   this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER \"AS IS\" AND ANY EXPRESS OR\nIMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO\nEVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\nOR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\nWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\nOTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\nADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n
Text Only
MIT License\n\nCopyright (c) 2022-Present, CHANfiG Contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n
Text Only
                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n

\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

"},{"location":"zh/about/","title":"\u5173\u4e8e","text":"

\u200b\u7531\u4e39\u7075\u200b\u5728\u200b\u5730\u7403\u200b\u5f00\u53d1\u200b

\u200b\u6211\u4eec\u200b\u662f\u200b\u4e00\u4e2a\u200b\u7531\u200b\u5f00\u53d1\u8005\u200b\u3001\u200b\u8bbe\u8ba1\u200b\u4eba\u5458\u200b\u548c\u200b\u5176\u4ed6\u200b\u4eba\u5458\u200b\u7ec4\u6210\u200b\u7684\u200b\u793e\u533a\u200b\uff0c\u200b\u81f4\u529b\u4e8e\u200b\u8ba9\u200b\u6df1\u5ea6\u200b\u5b66\u4e60\u200b\u6280\u672f\u200b\u66f4\u52a0\u200b\u5f00\u653e\u200b\u3002

\u200b\u6211\u4eec\u200b\u662f\u200b\u4e00\u4e2a\u200b\u7531\u200b\u4e2a\u4f53\u200b\u7ec4\u6210\u200b\u7684\u200b\u793e\u533a\u200b\uff0c\u200b\u81f4\u529b\u4e8e\u200b\u63a8\u52a8\u200b\u6df1\u5ea6\u200b\u5b66\u4e60\u200b\u7684\u200b\u53ef\u80fd\u6027\u200b\u8fb9\u754c\u200b\u3002

\u200b\u6211\u4eec\u200b\u5bf9\u200b\u6df1\u5ea6\u200b\u5b66\u4e60\u200b\u53ca\u5176\u200b\u7528\u6237\u200b\u5145\u6ee1\u200b\u6fc0\u60c5\u200b\u3002

\u200b\u6211\u4eec\u200b\u662f\u200b\u4e39\u7075\u200b\u3002

"},{"location":"zh/about/license/","title":"License","text":"

\u200b\u7ffb\u8bd1\u200b

\u200b\u672c\u6587\u200b\u5185\u5bb9\u200b\u4e3a\u200b\u7ffb\u8bd1\u200b\u7248\u672c\u200b\uff0c\u200b\u65e8\u5728\u200b\u4e3a\u200b\u7528\u6237\u200b\u63d0\u4f9b\u65b9\u4fbf\u200b\u3002 \u200b\u6211\u4eec\u200b\u5df2\u7ecf\u200b\u5c3d\u529b\u200b\u786e\u4fdd\u200b\u7ffb\u8bd1\u200b\u7684\u200b\u51c6\u786e\u6027\u200b\u3002 \u200b\u4f46\u200b\u8bf7\u200b\u6ce8\u610f\u200b\uff0c\u200b\u7ffb\u8bd1\u200b\u5185\u5bb9\u200b\u53ef\u80fd\u200b\u5305\u542b\u200b\u9519\u8bef\u200b\uff0c\u200b\u4ec5\u4f9b\u53c2\u8003\u200b\u3002 \u200b\u8bf7\u4ee5\u200b\u82f1\u6587\u200b\u539f\u6587\u200b\u4e3a\u51c6\u200b\u3002

\u200b\u4e3a\u200b\u6ee1\u8db3\u200b\u5408\u89c4\u6027\u200b\u4e0e\u200b\u6267\u6cd5\u200b\u8981\u6c42\u200b\uff0c\u200b\u7ffb\u8bd1\u200b\u6587\u6863\u200b\u4e2d\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e0d\u200b\u51c6\u786e\u200b\u6216\u200b\u6b67\u4e49\u200b\u4e4b\u5904\u200b\u5747\u200b\u4e0d\u200b\u5177\u6709\u200b\u7ea6\u675f\u529b\u200b\uff0c\u200b\u4e5f\u200b\u4e0d\u200b\u5177\u5907\u200b\u6cd5\u5f8b\u6548\u529b\u200b\u3002

"},{"location":"zh/about/license/#gnu-affero","title":"GNU AFFERO \u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1","text":"

\u200b\u7b2c\u200b3\u200b\u7248\u200b\uff0c2007\u200b\u5e74\u200b11\u200b\u6708\u200b19\u200b\u65e5\u200b

\u200b\u7248\u6743\u6240\u6709\u200b \u00a9 2007 Free Software Foundation, Inc. https://fsf.org/

Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

\u200b\u6bcf\u4e2a\u200b\u4eba\u200b\u90fd\u200b\u88ab\u200b\u5141\u8bb8\u200b\u590d\u5236\u200b\u548c\u200b\u5206\u53d1\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6587\u4ef6\u200b\u7684\u200b\u9010\u5b57\u200b\u526f\u672c\u200b\uff0c\u200b\u4f46\u200b\u4e0d\u200b\u5141\u8bb8\u200b\u8fdb\u884c\u200b\u66f4\u6539\u200b\u3002

"},{"location":"zh/about/license/#_1","title":"\u5e8f\u8a00","text":"

GNU Affero \u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u662f\u200b\u4e00\u4e2a\u200b\u81ea\u7531\u200b\u7684\u200b\u3001\u200b\u5141\u8bb8\u200b\u590d\u5236\u200b\u7684\u200b\u8f6f\u4ef6\u200b\u548c\u200b\u5176\u4ed6\u200b\u7c7b\u578b\u200b\u4f5c\u54c1\u200b\u7684\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u5728\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u8f6f\u4ef6\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u5b83\u200b\u662f\u200b\u4e13\u95e8\u200b\u4e3a\u200b\u786e\u4fdd\u200b\u4e0e\u200b\u793e\u533a\u200b\u5408\u4f5c\u200b\u800c\u200b\u8bbe\u8ba1\u200b\u3002

\u200b\u5927\u591a\u6570\u200b\u8f6f\u4ef6\u200b\u548c\u200b\u5176\u4ed6\u200b\u5b9e\u7528\u200b\u4f5c\u54c1\u200b\u7684\u200b\u8bb8\u53ef\u200b\u90fd\u200b\u662f\u200b\u4e3a\u4e86\u200b\u5265\u593a\u200b\u60a8\u200b\u5206\u4eab\u200b\u548c\u200b\u6539\u53d8\u200b\u4f5c\u54c1\u200b\u7684\u200b\u81ea\u7531\u200b\u3002\u200b\u76f8\u6bd4\u4e4b\u4e0b\u200b\uff0c\u200b\u6211\u4eec\u200b\u7684\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u76ee\u7684\u200b\u662f\u200b\u4fdd\u8bc1\u200b\u60a8\u200b\u5206\u4eab\u200b\u548c\u200b\u6539\u53d8\u200b\u4e00\u4e2a\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u6240\u6709\u200b\u7248\u672c\u200b\u7684\u200b\u81ea\u7531\u200b\u2013\u200b\u786e\u4fdd\u200b\u5b83\u200b\u5bf9\u200b\u6240\u6709\u200b\u7528\u6237\u200b\u90fd\u200b\u662f\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u3002

\u200b\u5f53\u200b\u6211\u4eec\u200b\u8c08\u8bba\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u65f6\u200b\uff0c\u200b\u6211\u4eec\u200b\u6307\u200b\u7684\u200b\u662f\u200b\u81ea\u7531\u200b\uff0c\u200b\u800c\u200b\u4e0d\u662f\u200b\u4ef7\u683c\u200b\u3002\u200b\u6211\u4eec\u200b\u7684\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u8bbe\u8ba1\u200b\u662f\u200b\u4e3a\u4e86\u200b\u786e\u4fdd\u60a8\u200b\u6709\u200b\u5206\u53d1\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u526f\u672c\u200b\u7684\u200b\u81ea\u7531\u200b\uff08\u200b\u5982\u679c\u200b\u60a8\u200b\u613f\u610f\u200b\uff0c\u200b\u8fd8\u200b\u53ef\u4ee5\u200b\u6536\u8d39\u200b\uff09\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u6536\u5230\u200b\u6e90\u4ee3\u7801\u200b\uff0c\u200b\u6216\u8005\u200b\u5982\u679c\u200b\u60a8\u200b\u60f3\u5f97\u5230\u200b\u5b83\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u6539\u53d8\u200b\u8f6f\u4ef6\u200b\u6216\u200b\u5728\u200b\u65b0\u200b\u7684\u200b\u81ea\u7531\u200b\u7a0b\u5e8f\u200b\u4e2d\u200b\u4f7f\u7528\u200b\u5b83\u200b\u7684\u200b\u7247\u6bb5\u200b\uff0c\u200b\u800c\u4e14\u200b\u60a8\u200b\u77e5\u9053\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u505a\u200b\u8fd9\u4e9b\u200b\u4e8b\u60c5\u200b\u3002

\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u5f00\u53d1\u8005\u200b\u901a\u8fc7\u200b\u4e24\u4e2a\u200b\u6b65\u9aa4\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b\u3002(1)\u200b\u4e3b\u5f20\u200b\u8f6f\u4ef6\u200b\u7684\u200b\u7248\u6743\u200b\uff0c(2)\u200b\u5411\u200b\u60a8\u200b\u63d0\u4f9b\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u5141\u8bb8\u200b\u60a8\u200b\u5408\u6cd5\u200b\u5730\u200b\u590d\u5236\u200b\u3001\u200b\u5206\u53d1\u200b\u548c\u200b/\u200b\u6216\u200b\u4fee\u6539\u200b\u8be5\u8f6f\u4ef6\u200b\u3002

\u200b\u634d\u536b\u200b\u6240\u6709\u200b\u7528\u6237\u200b\u81ea\u7531\u200b\u7684\u200b\u4e00\u4e2a\u200b\u6b21\u8981\u200b\u597d\u5904\u200b\u662f\u200b\uff0c\u200b\u5982\u679c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u66ff\u4ee3\u200b\u7248\u672c\u200b\u5f97\u5230\u200b\u5e7f\u6cdb\u200b\u4f7f\u7528\u200b\uff0c\u200b\u5c31\u200b\u53ef\u4ee5\u200b\u4f9b\u200b\u5176\u4ed6\u200b\u5f00\u53d1\u8005\u200b\u4f7f\u7528\u200b\u3002\u200b\u8bb8\u591a\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u7684\u200b\u5f00\u53d1\u8005\u200b\u5bf9\u200b\u7531\u6b64\u200b\u4ea7\u751f\u200b\u7684\u200b\u5408\u4f5c\u200b\u611f\u5230\u200b\u632f\u594b\u200b\u548c\u200b\u9f13\u821e\u200b\u3002\u200b\u7136\u800c\u200b\uff0c\u200b\u5728\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\u4f7f\u7528\u200b\u7684\u200b\u8f6f\u4ef6\u200b\uff0c\u200b\u8fd9\u79cd\u200b\u7ed3\u679c\u200b\u53ef\u80fd\u200b\u65e0\u6cd5\u200b\u5b9e\u73b0\u200b\u3002GNU\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u5141\u8bb8\u200b\u5236\u4f5c\u200b\u4e00\u4e2a\u200b\u4fee\u6539\u200b\u8fc7\u200b\u7684\u200b\u7248\u672c\u200b\uff0c\u200b\u8ba9\u200b\u516c\u4f17\u200b\u5728\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\u8bbf\u95ee\u200b\u5b83\u200b\uff0c\u200b\u800c\u200b\u4e0d\u200b\u9700\u8981\u200b\u5411\u200b\u516c\u4f17\u200b\u53d1\u5e03\u200b\u5176\u200b\u6e90\u4ee3\u7801\u200b\u3002

GNU Affero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u662f\u200b\u4e13\u95e8\u200b\u8bbe\u8ba1\u200b\u6765\u200b\u786e\u4fdd\u200b\u5728\u200b\u8fd9\u79cd\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u4fee\u6539\u200b\u540e\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u53ef\u4ee5\u200b\u88ab\u200b\u793e\u533a\u200b\u4f7f\u7528\u200b\u3002\u200b\u5b83\u200b\u8981\u6c42\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u200b\u7684\u200b\u8fd0\u8425\u5546\u200b\u5411\u200b\u8be5\u200b\u670d\u52a1\u5668\u200b\u7684\u200b\u7528\u6237\u200b\u63d0\u4f9b\u200b\u8fd0\u884c\u200b\u5728\u200b\u90a3\u91cc\u200b\u7684\u200b\u4fee\u6539\u200b\u7248\u672c\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u3002\u200b\u56e0\u6b64\u200b\uff0c\u200b\u5728\u200b\u4e00\u4e2a\u200b\u53ef\u200b\u516c\u5f00\u200b\u8bbf\u95ee\u200b\u7684\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\u516c\u5f00\u200b\u4f7f\u7528\u200b\u4e00\u4e2a\u200b\u4fee\u6539\u200b\u8fc7\u200b\u7684\u200b\u7248\u672c\u200b\uff0c\u200b\u4f7f\u200b\u516c\u4f17\u200b\u80fd\u591f\u200b\u83b7\u5f97\u200b\u4fee\u6539\u200b\u8fc7\u200b\u7684\u200b\u7248\u672c\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u3002

\u200b\u4e00\u4e2a\u200b\u8f83\u200b\u65e9\u200b\u7684\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u79f0\u4e3a\u200bAffero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u7531\u200bAffero\u200b\u53d1\u5e03\u200b\uff0c\u200b\u65e8\u5728\u200b\u5b9e\u73b0\u200b\u7c7b\u4f3c\u200b\u76ee\u6807\u200b\u3002\u200b\u8fd9\u662f\u200b\u4e00\u4e2a\u200b\u4e0d\u540c\u200b\u7684\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u4e0d\u662f\u200bAffero GPL\u200b\u7684\u200b\u4e00\u4e2a\u200b\u7248\u672c\u200b\uff0c\u200b\u4f46\u200bAffero\u200b\u5df2\u7ecf\u200b\u53d1\u5e03\u200b\u4e86\u200bAffero GPL\u200b\u7684\u200b\u4e00\u4e2a\u200b\u65b0\u200b\u7248\u672c\u200b\uff0c\u200b\u5141\u8bb8\u200b\u5728\u200b\u8fd9\u4e2a\u200b\u8bb8\u53ef\u8bc1\u200b\u4e0b\u200b\u91cd\u65b0\u200b\u8bb8\u53ef\u200b\u3002

\u200b\u5173\u4e8e\u200b\u590d\u5236\u200b\u3001\u200b\u5206\u53d1\u200b\u548c\u200b\u4fee\u6539\u200b\u7684\u200b\u786e\u5207\u200b\u6761\u6b3e\u200b\u548c\u200b\u6761\u4ef6\u200b\u5982\u4e0b\u200b\u3002

"},{"location":"zh/about/license/#_2","title":"\u6761\u6b3e\u200b\u4e0e\u200b\u6761\u4ef6","text":""},{"location":"zh/about/license/#0","title":"0. \u200b\u5b9a\u4e49\u200b.","text":"

\u201c\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u201d \u200b\u662f\u200b\u6307\u200bGNU Affero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u7b2c\u4e09\u7248\u200b\u3002 \u201c\u200b\u7248\u6743\u200b\u201d \u200b\u4e5f\u200b\u6307\u200b\u9002\u7528\u200b\u4e8e\u200b\u5176\u4ed6\u200b\u7c7b\u578b\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7c7b\u4f3c\u200b\u7248\u6743\u200b\u7684\u200b\u6cd5\u5f8b\u200b\uff0c\u200b\u5982\u200b\u534a\u5bfc\u4f53\u200b\u63a9\u6a21\u200b\u3002

\u201c\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u5728\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e0b\u200b\u8bb8\u53ef\u200b\u7684\u200b\u4efb\u4f55\u200b\u6709\u200b\u7248\u6743\u200b\u7684\u200b\u4f5c\u54c1\u200b\u3002\u200b\u6bcf\u4e2a\u200b\u88ab\u200b\u8bb8\u53ef\u200b\u4eba\u200b\u90fd\u200b\u88ab\u200b\u79f0\u547c\u200b\u4e3a\u200b \u201c\u200b\u60a8\u200b\u201d\u3002\u201d\u200b\u88ab\u200b\u8bb8\u53ef\u200b\u4eba\u200b\u201d \u200b\u548c\u200b \u201c\u200b\u63a5\u53d7\u8005\u200b\u201d \u200b\u53ef\u4ee5\u200b\u662f\u200b\u4e2a\u4eba\u200b\u6216\u200b\u7ec4\u7ec7\u200b\u3002

\u201c\u200b\u4fee\u6539\u200b\u201d \u200b\u4f5c\u54c1\u200b\u662f\u200b\u6307\u4ee5\u200b\u9700\u8981\u200b\u7248\u6743\u200b\u8bb8\u53ef\u200b\u7684\u200b\u65b9\u5f0f\u200b\u590d\u5236\u200b\u6216\u200b\u6539\u7f16\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u7684\u200b\u5168\u90e8\u200b\u6216\u200b\u90e8\u5206\u200b\u5185\u5bb9\u200b\uff0c\u200b\u800c\u200b\u4e0d\u662f\u200b\u5236\u4f5c\u200b\u4e00\u4e2a\u200b\u5b8c\u5168\u200b\u7684\u200b\u526f\u672c\u200b\u3002\u200b\u7531\u6b64\u200b\u4ea7\u751f\u200b\u7684\u200b\u4f5c\u54c1\u200b\u88ab\u200b\u79f0\u4e3a\u200b\u65e9\u671f\u200b\u4f5c\u54c1\u200b\u7684\u200b \u201c\u200b\u4fee\u6539\u7248\u200b\u201d \u200b\u6216\u200b \u201c\u200b\u57fa\u4e8e\u200b\u201d \u200b\u65e9\u671f\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4f5c\u54c1\u200b\u3002 \u200b\u4e00\u4e2a\u200b \u201c\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u672a\u7ecf\u200b\u4fee\u6539\u200b\u7684\u200b\u7a0b\u5e8f\u200b\u6216\u200b\u57fa\u4e8e\u200b\u8be5\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u4f5c\u54c1\u200b\u3002

\u201c\u200b\u4f20\u64ad\u200b\u201d \u200b\u4f5c\u54c1\u200b\u662f\u200b\u6307\u200b\u5728\u200b\u672a\u7ecf\u8bb8\u53ef\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u5bf9\u200b\u4f5c\u54c1\u200b\u505a\u200b\u4efb\u4f55\u200b\u4e8b\u60c5\u200b\uff0c\u200b\u4f7f\u200b\u60a8\u200b\u5728\u200b\u9002\u7528\u200b\u7684\u200b\u7248\u6743\u6cd5\u200b\u4e0b\u200b\u627f\u62c5\u200b\u76f4\u63a5\u200b\u6216\u200b\u95f4\u63a5\u200b\u7684\u200b\u4fb5\u6743\u200b\u8d23\u4efb\u200b\uff0c\u200b\u4f46\u200b\u5728\u200b\u8ba1\u7b97\u673a\u200b\u4e0a\u200b\u6267\u884c\u200b\u6216\u200b\u4fee\u6539\u200b\u79c1\u4eba\u200b\u526f\u672c\u200b\u9664\u5916\u200b\u3002\u200b\u4f20\u64ad\u200b\u5305\u62ec\u200b\u590d\u5236\u200b\u3001\u200b\u5206\u53d1\u200b\uff08\u200b\u65e0\u8bba\u200b\u662f\u5426\u200b\u4fee\u6539\u200b\uff09\u3001\u200b\u5411\u200b\u516c\u4f17\u200b\u63d0\u4f9b\u200b\uff0c\u200b\u5728\u200b\u4e00\u4e9b\u200b\u56fd\u5bb6\u200b\u8fd8\u200b\u5305\u62ec\u200b\u5176\u4ed6\u200b\u6d3b\u52a8\u200b\u3002 \u200b\u4f20\u64ad\u200b\u201d \u200b\u4f5c\u54c1\u200b\u662f\u200b\u6307\u4f7f\u200b\u5176\u4ed6\u200b\u5404\u65b9\u200b\u80fd\u591f\u200b\u5236\u4f5c\u200b\u6216\u200b\u63a5\u53d7\u200b\u526f\u672c\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e00\u79cd\u200b\u4f20\u64ad\u200b\u3002\u200b\u4ec5\u4ec5\u200b\u662f\u200b\u901a\u8fc7\u200b\u8ba1\u7b97\u673a\u7f51\u7edc\u200b\u4e0e\u200b\u7528\u6237\u200b\u4e92\u52a8\u200b\uff0c\u200b\u800c\u200b\u6ca1\u6709\u200b\u8f6c\u8ba9\u200b\u526f\u672c\u200b\uff0c\u200b\u5e76\u200b\u4e0d\u662f\u200b\u4f20\u64ad\u200b\u3002

\u200b\u4ea4\u4e92\u5f0f\u200b\u7528\u6237\u754c\u9762\u200b\u663e\u793a\u200b \u201c\u200b\u9002\u5f53\u200b\u7684\u200b\u6cd5\u5f8b\u200b\u58f0\u660e\u200b\u201d \u200b\u7684\u200b\u7a0b\u5ea6\u200b\u662f\u200b\uff0c\u200b\u5b83\u200b\u5305\u62ec\u200b\u4e00\u4e2a\u200b\u65b9\u4fbf\u200b\u548c\u200b\u663e\u773c\u200b\u7684\u200b\u529f\u80fd\u200b\uff0c(1)\u200b\u663e\u793a\u200b\u9002\u5f53\u200b\u7684\u200b\u7248\u6743\u200b\u58f0\u660e\u200b\uff0c(2)\u200b\u544a\u8bc9\u200b\u7528\u6237\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u6ca1\u6709\u200b\u4fdd\u8bc1\u200b\uff08\u200b\u9664\u4e86\u200b\u63d0\u4f9b\u200b\u4fdd\u8bc1\u200b\u7684\u200b\u8303\u56f4\u200b\uff09\uff0c\u200b\u88ab\u200b\u8bb8\u53ef\u200b\u4eba\u200b\u53ef\u4ee5\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u200b\u4f20\u8fbe\u200b\u8be5\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u5982\u4f55\u200b\u67e5\u770b\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u526f\u672c\u200b\u3002\u200b\u5982\u679c\u200b\u754c\u9762\u200b\u5448\u73b0\u200b\u7684\u200b\u662f\u200b\u4e00\u4e2a\u200b\u7528\u6237\u200b\u547d\u4ee4\u200b\u6216\u200b\u9009\u9879\u200b\u7684\u200b\u5217\u8868\u200b\uff0c\u200b\u5982\u200b\u83dc\u5355\u200b\uff0c\u200b\u90a3\u4e48\u200b\u5217\u8868\u200b\u4e2d\u200b\u7684\u200b\u7a81\u51fa\u200b\u9879\u76ee\u200b\u5c31\u200b\u7b26\u5408\u200b\u8fd9\u4e00\u200b\u6807\u51c6\u200b\u3002

"},{"location":"zh/about/license/#1","title":"1. \u200b\u6e90\u4ee3\u7801\u200b.","text":"

\u200b\u4f5c\u54c1\u200b\u7684\u200b \u201c\u200b\u6e90\u4ee3\u7801\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u5bf9\u200b\u4f5c\u54c1\u200b\u8fdb\u884c\u200b\u4fee\u6539\u200b\u7684\u200b\u9996\u9009\u200b\u5f62\u5f0f\u200b\u3002\u201d\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4efb\u4f55\u200b\u975e\u200b\u6e90\u7801\u200b\u5f62\u5f0f\u200b\u3002

\u201c\u200b\u6807\u51c6\u63a5\u53e3\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u7531\u200b\u516c\u8ba4\u200b\u7684\u200b\u6807\u51c6\u200b\u673a\u6784\u200b\u5b9a\u4e49\u200b\u7684\u200b\u5b98\u65b9\u200b\u6807\u51c6\u200b\u7684\u200b\u63a5\u53e3\u200b\uff0c\u200b\u6216\u8005\u200b\u5728\u200b\u4e3a\u200b\u67d0\u200b\u4e00\u200b\u7279\u5b9a\u200b\u7f16\u7a0b\u8bed\u8a00\u200b\u6307\u5b9a\u200b\u63a5\u53e3\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u6307\u5728\u200b\u4f7f\u7528\u200b\u8be5\u200b\u8bed\u8a00\u200b\u7684\u200b\u5f00\u53d1\u8005\u200b\u4e2d\u200b\u5e7f\u6cdb\u200b\u4f7f\u7528\u200b\u7684\u200b\u63a5\u53e3\u200b\u3002

\u200b\u53ef\u200b\u6267\u884c\u200b\u4f5c\u54c1\u200b\u7684\u200b \u201c\u200b\u7cfb\u7edf\u200b\u5e93\u200b\u201d \u200b\u5305\u62ec\u200b\u9664\u200b\u4f5c\u54c1\u200b\u6574\u4f53\u200b\u4ee5\u5916\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e1c\u897f\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u4e1c\u897f\u200b(a)\u200b\u4ee5\u200b\u6b63\u5e38\u200b\u7684\u200b\u5f62\u5f0f\u200b\u6253\u5305\u200b\u4e00\u4e2a\u200b\u4e3b\u8981\u200b\u90e8\u4ef6\u200b\uff0c\u200b\u4f46\u200b\u4e0d\u662f\u200b\u8be5\u200b\u4e3b\u8981\u200b\u90e8\u4ef6\u200b\u7684\u200b\u4e00\u90e8\u5206\u200b\uff0c\u200b\u4ee5\u53ca\u200b(b)\u200b\u4ec5\u200b\u7528\u4e8e\u200b\u4f7f\u200b\u4f5c\u54c1\u200b\u4e0e\u200b\u8be5\u200b\u4e3b\u8981\u200b\u90e8\u4ef6\u200b\u4e00\u8d77\u200b\u4f7f\u7528\u200b\uff0c\u200b\u6216\u200b\u7528\u4e8e\u200b\u5b9e\u73b0\u200b\u4e00\u4e2a\u200b\u6807\u51c6\u63a5\u53e3\u200b\uff0c\u200b\u8be5\u200b\u63a5\u53e3\u200b\u7684\u200b\u5b9e\u73b0\u200b\u5df2\u4ee5\u200b\u6e90\u4ee3\u7801\u200b\u5f62\u5f0f\u200b\u5411\u200b\u516c\u4f17\u200b\u63d0\u4f9b\u200b\u3002\u200b\u8fd9\u91cc\u200b\u7684\u200b \u201c\u200b\u4e3b\u8981\u200b\u90e8\u4ef6\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u53ef\u200b\u6267\u884c\u200b\u4f5c\u54c1\u200b\u6240\u200b\u8fd0\u884c\u200b\u7684\u200b\u7279\u5b9a\u200b\u64cd\u4f5c\u7cfb\u7edf\u200b\uff08\u200b\u5982\u679c\u200b\u6709\u200b\u7684\u8bdd\u200b\uff09\u200b\u7684\u200b\u4e3b\u8981\u200b\u57fa\u672c\u200b\u90e8\u4ef6\u200b\uff08\u200b\u5185\u6838\u200b\u3001\u200b\u7a97\u53e3\u200b\u7cfb\u7edf\u200b\u7b49\u200b\uff09\uff0c\u200b\u6216\u200b\u7528\u4e8e\u200b\u5236\u4f5c\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7f16\u8bd1\u5668\u200b\uff0c\u200b\u6216\u200b\u7528\u4e8e\u200b\u8fd0\u884c\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u7684\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u89e3\u91ca\u5668\u200b\u3002

\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u5f62\u5f0f\u200b\u7684\u200b\u4f5c\u54c1\u200b\u7684\u200b \u201c\u200b\u76f8\u5e94\u200b\u6e90\u4ee3\u7801\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u751f\u6210\u200b\u3001\u200b\u5b89\u88c5\u200b\u548c\u200b\uff08\u200b\u5bf9\u4e8e\u200b\u53ef\u200b\u6267\u884c\u200b\u4f5c\u54c1\u200b\uff09\u200b\u8fd0\u884c\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u4ee5\u53ca\u200b\u4fee\u6539\u200b\u4f5c\u54c1\u200b\u6240\u200b\u9700\u200b\u7684\u200b\u6240\u6709\u200b\u6e90\u4ee3\u7801\u200b\uff0c\u200b\u5305\u62ec\u200b\u63a7\u5236\u200b\u8fd9\u4e9b\u200b\u6d3b\u52a8\u200b\u7684\u200b\u811a\u672c\u200b\u3002\u200b\u4f46\u662f\u200b\uff0c\u200b\u5b83\u200b\u4e0d\u200b\u5305\u62ec\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7cfb\u7edf\u200b\u5e93\u200b\uff0c\u200b\u4e5f\u200b\u4e0d\u200b\u5305\u62ec\u200b\u5728\u200b\u6267\u884c\u200b\u8fd9\u4e9b\u200b\u6d3b\u52a8\u200b\u65f6\u200b\u672a\u7ecf\u200b\u4fee\u6539\u200b\u4f46\u200b\u4e0d\u200b\u5c5e\u4e8e\u200b\u4f5c\u54c1\u200b\u7684\u200b\u901a\u7528\u200b\u5de5\u5177\u200b\u6216\u200b\u666e\u904d\u200b\u53ef\u7528\u200b\u7684\u200b\u514d\u8d39\u200b\u7a0b\u5e8f\u200b\u3002\u200b\u4f8b\u5982\u200b\uff0c\u200b\u76f8\u5e94\u200b\u6e90\u200b\u5305\u62ec\u200b\u4e0e\u200b\u4f5c\u54c1\u200b\u7684\u200b\u6e90\u6587\u4ef6\u200b\u76f8\u5173\u200b\u7684\u200b\u63a5\u53e3\u5b9a\u4e49\u200b\u6587\u4ef6\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u4f5c\u54c1\u200b\u4e13\u95e8\u200b\u8bbe\u8ba1\u200b\u7684\u200b\u5171\u4eab\u200b\u5e93\u200b\u548c\u200b\u52a8\u6001\u200b\u94fe\u63a5\u200b\u7684\u200b\u5b50\u7a0b\u5e8f\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\uff0c\u200b\u4f8b\u5982\u200b\u901a\u8fc7\u200b\u4eb2\u5bc6\u200b\u7684\u200b\u6570\u636e\u901a\u4fe1\u200b\u6216\u200b\u63a7\u5236\u6d41\u200b\u5728\u200b\u8fd9\u4e9b\u200b\u5b50\u7a0b\u5e8f\u200b\u548c\u200b\u4f5c\u54c1\u200b\u7684\u200b\u5176\u4ed6\u200b\u90e8\u5206\u200b\u4e4b\u95f4\u200b\u3002

\u200b\u76f8\u5e94\u200b\u6e90\u200b\u4e0d\u200b\u9700\u8981\u200b\u5305\u62ec\u200b\u7528\u6237\u200b\u53ef\u4ee5\u200b\u4ece\u200b\u76f8\u5e94\u200b\u6e90\u200b\u7684\u200b\u5176\u4ed6\u200b\u90e8\u5206\u200b\u81ea\u52a8\u200b\u91cd\u65b0\u200b\u751f\u6210\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e1c\u897f\u200b\u3002

\u200b\u6e90\u4ee3\u7801\u200b\u5f62\u5f0f\u200b\u7684\u200b\u4f5c\u54c1\u200b\u7684\u200b\u76f8\u5e94\u200b\u6e90\u200b\u662f\u200b\u6307\u200b\u540c\u4e00\u200b\u4f5c\u54c1\u200b\u3002

"},{"location":"zh/about/license/#2","title":"2. \u200b\u57fa\u672c\u200b\u6743\u9650\u200b.","text":"

\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6388\u4e88\u200b\u7684\u200b\u6240\u6709\u200b\u6743\u5229\u200b\u90fd\u200b\u662f\u200b\u5728\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u7248\u6743\u200b\u671f\u9650\u5185\u200b\u6388\u4e88\u200b\u7684\u200b\uff0c\u200b\u5e76\u4e14\u200b\u5728\u200b\u6ee1\u8db3\u200b\u6240\u8ff0\u200b\u6761\u4ef6\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u662f\u200b\u4e0d\u53ef\u200b\u64a4\u6d88\u200b\u7684\u200b\u3002\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u660e\u786e\u200b\u80af\u5b9a\u200b\u4e86\u200b\u60a8\u200b\u5bf9\u200b\u8fd0\u884c\u200b\u672a\u7ecf\u200b\u4fee\u6539\u200b\u7684\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u65e0\u9650\u200b\u8bb8\u53ef\u200b\u3002\u200b\u53ea\u6709\u200b\u5728\u200b\u8f93\u51fa\u200b\u7684\u200b\u5185\u5bb9\u200b\u6784\u6210\u200b\u4e86\u200b\u4e00\u4e2a\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u8fd0\u884c\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u8f93\u51fa\u200b\u624d\u200b\u53d7\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u4fdd\u62a4\u200b\u3002\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u627f\u8ba4\u200b\u60a8\u200b\u7684\u200b\u5408\u7406\u200b\u4f7f\u7528\u6743\u200b\u6216\u200b\u7248\u6743\u6cd5\u200b\u6240\u200b\u89c4\u5b9a\u200b\u7684\u200b\u5176\u4ed6\u200b\u540c\u7b49\u200b\u6743\u5229\u200b\u3002

\u200b\u53ea\u8981\u200b\u60a8\u200b\u7684\u200b\u8bb8\u53ef\u8bc1\u200b\u4ecd\u7136\u200b\u6709\u6548\u200b\uff0c\u200b\u53ef\u4ee5\u200b\u65e0\u6761\u4ef6\u200b\u5730\u200b\u5236\u4f5c\u200b\u3001\u200b\u8fd0\u884c\u200b\u548c\u200b\u4f20\u64ad\u200b\u60a8\u200b\u6ca1\u6709\u200b\u8f6c\u8fbe\u200b\u7684\u200b\u6db5\u76d6\u200b\u4f5c\u54c1\u200b\u3002\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u5c06\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u4f20\u8fbe\u200b\u7ed9\u200b\u5176\u4ed6\u4eba\u200b\uff0c\u200b\u552f\u4e00\u200b\u7684\u200b\u76ee\u7684\u200b\u662f\u200b\u8ba9\u200b\u4ed6\u4eec\u200b\u4e13\u95e8\u200b\u4e3a\u200b\u60a8\u200b\u8fdb\u884c\u200b\u4fee\u6539\u200b\uff0c\u200b\u6216\u200b\u4e3a\u200b\u60a8\u200b\u63d0\u4f9b\u200b\u8fd0\u884c\u200b\u8fd9\u4e9b\u200b\u4f5c\u54c1\u200b\u7684\u200b\u8bbe\u65bd\u200b\uff0c\u200b\u524d\u63d0\u200b\u662f\u200b\u60a8\u200b\u5728\u200b\u4f20\u8fbe\u200b\u6240\u6709\u200b\u60a8\u200b\u4e0d\u200b\u63a7\u5236\u200b\u7248\u6743\u200b\u7684\u200b\u6750\u6599\u200b\u65f6\u200b\u9075\u5b88\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u6761\u6b3e\u200b\u3002\u200b\u90a3\u4e9b\u200b\u4e3a\u200b\u60a8\u200b\u5236\u4f5c\u200b\u6216\u200b\u8fd0\u884c\u200b\u6240\u6d89\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4eba\u200b\u5fc5\u987b\u200b\u5b8c\u5168\u200b\u4ee3\u8868\u200b\u60a8\u200b\uff0c\u200b\u5728\u200b\u60a8\u200b\u7684\u200b\u6307\u5bfc\u200b\u548c\u200b\u63a7\u5236\u200b\u4e0b\u200b\uff0c\u200b\u6309\u7167\u200b\u7981\u6b62\u200b\u4ed6\u4eec\u200b\u5728\u200b\u4e0e\u200b\u60a8\u200b\u7684\u200b\u5173\u7cfb\u200b\u4e4b\u5916\u200b\u5236\u4f5c\u200b\u60a8\u200b\u7684\u200b\u7248\u6743\u200b\u6750\u6599\u200b\u7684\u200b\u4efb\u4f55\u200b\u526f\u672c\u200b\u7684\u200b\u6761\u6b3e\u200b\u6765\u200b\u8fdb\u884c\u200b\u3002

\u200b\u5728\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u4ec5\u200b\u5728\u200b\u4e0b\u8ff0\u200b\u6761\u4ef6\u200b\u4e0b\u200b\u5141\u8bb8\u200b\u8f6c\u8ba9\u200b\u3002\u200b\u4e0d\u200b\u5141\u8bb8\u200b\u8f6c\u200b\u6388\u6743\u200b\uff1b\u200b\u7b2c\u200b10\u200b\u6761\u200b\u89c4\u5b9a\u200b\u6ca1\u6709\u200b\u5fc5\u8981\u200b\u3002

"},{"location":"zh/about/license/#3","title":"3. \u200b\u4ece\u200b\u53cd\u200b\u89c4\u907f\u200b\u6cd5\u4e2d\u200b\u4fdd\u62a4\u200b\u7528\u6237\u200b\u7684\u200b\u5408\u6cd5\u6743\u5229\u200b.","text":"

\u200b\u6839\u636e\u200b\u4efb\u4f55\u200b\u5c65\u884c\u200b1996\u200b\u5e74\u200b12\u200b\u6708\u200b20\u200b\u65e5\u200b\u901a\u8fc7\u200b\u7684\u200b\u4e16\u754c\u77e5\u8bc6\u4ea7\u6743\u7ec4\u7ec7\u200b\u7248\u6743\u200b\u6761\u7ea6\u200b\u7b2c\u200b11\u200b\u6761\u200b\u89c4\u5b9a\u200b\u7684\u200b\u4e49\u52a1\u200b\u7684\u200b\u9002\u7528\u6cd5\u5f8b\u200b\uff0c\u200b\u6216\u200b\u7981\u6b62\u200b\u6216\u200b\u9650\u5236\u200b\u89c4\u907f\u200b\u6b64\u7c7b\u200b\u63aa\u65bd\u200b\u7684\u200b\u7c7b\u4f3c\u200b\u6cd5\u5f8b\u200b\uff0c\u200b\u4efb\u4f55\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u90fd\u200b\u4e0d\u5f97\u200b\u88ab\u200b\u89c6\u4e3a\u200b\u6709\u6548\u200b\u6280\u672f\u200b\u63aa\u65bd\u200b\u7684\u200b\u4e00\u90e8\u5206\u200b\u3002

\u200b\u5f53\u200b\u60a8\u200b\u4f20\u8fbe\u200b\u4e00\u4e2a\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u65f6\u200b\uff0c\u200b\u60a8\u200b\u653e\u5f03\u200b\u4efb\u4f55\u200b\u7981\u6b62\u200b\u89c4\u907f\u200b\u6280\u672f\u200b\u63aa\u65bd\u200b\u7684\u200b\u6cd5\u5f8b\u200b\u6743\u529b\u200b\uff0c\u200b\u53ea\u8981\u200b\u8fd9\u79cd\u200b\u89c4\u907f\u200b\u662f\u200b\u901a\u8fc7\u200b\u5bf9\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u884c\u4f7f\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e0b\u200b\u7684\u200b\u6743\u5229\u200b\u800c\u200b\u5b9e\u73b0\u200b\u7684\u200b\uff0c\u200b\u5e76\u4e14\u200b\u60a8\u200b\u5426\u8ba4\u200b\u6709\u200b\u4efb\u4f55\u200b\u9650\u5236\u200b\u64cd\u4f5c\u200b\u6216\u200b\u4fee\u6539\u200b\u4f5c\u54c1\u200b\u7684\u200b\u610f\u56fe\u200b\uff0c\u200b\u4ee5\u200b\u4f5c\u4e3a\u200b\u5bf9\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7528\u6237\u200b\u5f3a\u5236\u6267\u884c\u200b\u60a8\u200b\u6216\u200b\u7b2c\u4e09\u65b9\u200b\u7981\u6b62\u200b\u89c4\u907f\u200b\u6280\u672f\u200b\u63aa\u65bd\u200b\u7684\u200b\u6cd5\u5f8b\u200b\u6743\u5229\u200b\u7684\u200b\u624b\u6bb5\u200b\u3002

"},{"location":"zh/about/license/#4","title":"4. \u200b\u4f20\u9012\u200b\u9010\u5b57\u200b\u62f7\u8d1d\u200b.","text":"

\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u5728\u200b\u6536\u5230\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u540e\u200b\uff0c\u200b\u4ee5\u200b\u4efb\u4f55\u200b\u5a92\u4ecb\u200b\u4f20\u9012\u200b\u5176\u200b\u9010\u5b57\u200b\u62f7\u8d1d\u200b\uff0c\u200b\u4f46\u200b\u60a8\u200b\u5fc5\u987b\u200b\u5728\u200b\u6bcf\u4efd\u200b\u62f7\u8d1d\u200b\u4e0a\u200b\u9192\u76ee\u200b\u5730\u200b\u3001\u200b\u9002\u5f53\u200b\u5730\u200b\u53d1\u5e03\u200b\u9002\u5f53\u200b\u7684\u200b\u7248\u6743\u200b\u58f0\u660e\u200b\uff1b\u200b\u4fdd\u6301\u200b\u6240\u6709\u200b\u8bf4\u660e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u548c\u200b\u6839\u636e\u200b\u7b2c\u200b7\u200b\u6761\u200b\u6dfb\u52a0\u200b\u7684\u200b\u4efb\u4f55\u200b\u975e\u200b\u8bb8\u53ef\u200b\u6761\u6b3e\u200b\u9002\u7528\u200b\u4e8e\u200b\u4ee3\u7801\u200b\u7684\u200b\u58f0\u660e\u200b\u5b8c\u6574\u65e0\u7f3a\u200b\uff1b\u200b\u4fdd\u6301\u200b\u6240\u6709\u200b\u5173\u4e8e\u200b\u6ca1\u6709\u200b\u4efb\u4f55\u200b\u4fdd\u8bc1\u200b\u7684\u200b\u58f0\u660e\u200b\u5b8c\u6574\u65e0\u7f3a\u200b\uff1b\u200b\u5e76\u200b\u5c06\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u62f7\u8d1d\u200b\u4e0e\u200b\u7a0b\u5e8f\u200b\u4e00\u8d77\u200b\u4ea4\u7ed9\u200b\u6240\u6709\u200b\u63a5\u6536\u8005\u200b\u3002

\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u5bf9\u200b\u6bcf\u4efd\u200b\u62f7\u8d1d\u200b\u6536\u53d6\u200b\u4efb\u4f55\u200b\u8d39\u7528\u200b\uff0c\u200b\u4e5f\u200b\u53ef\u4ee5\u200b\u4e0d\u200b\u6536\u53d6\u200b\u4efb\u4f55\u200b\u8d39\u7528\u200b\uff0c\u200b\u60a8\u200b\u8fd8\u200b\u53ef\u4ee5\u200b\u63d0\u4f9b\u200b\u6709\u507f\u200b\u7684\u200b\u652f\u6301\u200b\u6216\u200b\u4fdd\u4fee\u200b\u4fdd\u62a4\u200b\u3002

"},{"location":"zh/about/license/#5","title":"5. \u200b\u4f20\u9012\u200b\u4fee\u6539\u200b\u540e\u200b\u7684\u200b\u6e90\u200b\u7248\u672c\u200b.","text":"

\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u6839\u636e\u200b\u7b2c\u200b 4 \u200b\u8282\u200b\u7684\u200b\u6761\u6b3e\u200b\uff0c\u200b\u4ee5\u200b\u6e90\u4ee3\u7801\u200b\u7684\u200b\u5f62\u5f0f\u200b\u4f20\u8fbe\u200b\u57fa\u4e8e\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u6216\u200b\u6839\u636e\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u4fee\u6539\u200b\u800c\u200b\u4ea7\u751f\u200b\u7684\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u4f46\u200b\u60a8\u200b\u5fc5\u987b\u200b\u6ee1\u8db3\u200b\u4ee5\u4e0b\u200b\u6240\u6709\u200b\u6761\u4ef6\u200b:

a) \u200b\u4f5c\u54c1\u200b\u5fc5\u987b\u200b\u6709\u200b\u9192\u76ee\u200b\u7684\u200b\u58f0\u660e\u200b\uff0c\u200b\u8bf4\u660e\u200b\u60a8\u200b\u4fee\u6539\u200b\u4e86\u200b\u5b83\u200b\uff0c\u200b\u5e76\u200b\u7ed9\u51fa\u200b\u76f8\u5173\u200b\u7684\u200b\u65e5\u671f\u200b\u3002 b) \u200b\u4f5c\u54c1\u200b\u5fc5\u987b\u200b\u6709\u200b\u9192\u76ee\u200b\u7684\u200b\u58f0\u660e\u200b\uff0c\u200b\u8bf4\u660e\u200b\u5b83\u200b\u662f\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u548c\u200b\u6839\u636e\u200b\u7b2c\u200b7\u200b\u6761\u200b\u589e\u52a0\u200b\u7684\u200b\u6761\u4ef6\u200b\u53d1\u5e03\u200b\u7684\u200b\u3002\u200b\u8fd9\u4e00\u200b\u8981\u6c42\u200b\u4fee\u6539\u200b\u4e86\u200b\u7b2c\u200b4\u200b\u8282\u4e2d\u200b \u201c\u200b\u4fdd\u6301\u200b\u6240\u6709\u200b\u901a\u77e5\u200b\u7684\u200b\u5b8c\u6574\u6027\u200b\u201d \u200b\u7684\u200b\u8981\u6c42\u200b\u3002 c) \u200b\u60a8\u200b\u5fc5\u987b\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u5c06\u200b\u6574\u4e2a\u200b\u4f5c\u54c1\u200b\u4f5c\u4e3a\u200b\u4e00\u4e2a\u200b\u6574\u4f53\u200b\u8bb8\u53ef\u200b\u7ed9\u200b\u4efb\u4f55\u200b\u62e5\u6709\u200b\u5176\u200b\u526f\u672c\u200b\u7684\u200b\u4eba\u200b\u3002\u200b\u56e0\u6b64\u200b\uff0c\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u5c06\u200b\u4e0e\u200b\u4efb\u4f55\u200b\u9002\u7528\u200b\u7684\u200b\u7b2c\u200b7\u200b\u6761\u200b\u9644\u52a0\u200b\u6761\u6b3e\u200b\u4e00\u8d77\u200b\uff0c\u200b\u9002\u7528\u200b\u4e8e\u200b\u6574\u4e2a\u200b\u4f5c\u54c1\u200b\u53ca\u5176\u200b\u6240\u6709\u200b\u90e8\u5206\u200b\uff0c\u200b\u65e0\u8bba\u200b\u5b83\u4eec\u200b\u662f\u200b\u5982\u4f55\u200b\u5305\u88c5\u200b\u7684\u200b\u3002\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e0d\u200b\u5141\u8bb8\u200b\u4ee5\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u65b9\u5f0f\u200b\u8bb8\u53ef\u200b\u8be5\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u4f46\u200b\u5982\u679c\u200b\u60a8\u200b\u5df2\u7ecf\u200b\u5355\u72ec\u200b\u6536\u5230\u200b\u4e86\u200b\u8fd9\u79cd\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u5b83\u200b\u4e5f\u200b\u4e0d\u4f1a\u200b\u4f7f\u200b\u8fd9\u79cd\u200b\u8bb8\u53ef\u200b\u5931\u6548\u200b\u3002 \u200b\u5982\u679c\u200b\u4e00\u4e2a\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u4e0e\u200b\u5176\u4ed6\u200b\u5355\u72ec\u200b\u548c\u200b\u72ec\u7acb\u200b\u7684\u200b\u4f5c\u54c1\u200b\u7684\u200b\u6c47\u7f16\u200b\uff0c\u200b\u5176\u200b\u6027\u8d28\u200b\u4e0d\u662f\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u5ef6\u4f38\u200b\uff0c\u200b\u5e76\u4e14\u200b\u6ca1\u6709\u200b\u4e0e\u200b\u4e4b\u200b\u7ed3\u5408\u200b\u4ee5\u200b\u5f62\u6210\u200b\u66f4\u5927\u200b\u7684\u200b\u7a0b\u5e8f\u200b\uff0c\u200b\u5728\u200b\u5b58\u50a8\u200b\u6216\u200b\u5206\u53d1\u200b\u5a92\u4ecb\u200b\u7684\u200b\u67d0\u200b\u4e00\u5377\u200b\u4e0a\u200b\uff0c\u200b\u5982\u679c\u200b\u8be5\u200b\u6c47\u7f16\u200b\u53ca\u5176\u200b\u4ea7\u751f\u200b\u7684\u200b\u7248\u6743\u200b\u6ca1\u6709\u200b\u88ab\u200b\u7528\u6765\u200b\u9650\u5236\u200b\u6c47\u7f16\u200b\u7528\u6237\u200b\u7684\u200b\u8bbf\u95ee\u200b\u6216\u200b\u6cd5\u5f8b\u200b\u6743\u5229\u200b\uff0c\u200b\u8d85\u51fa\u200b\u5355\u4e2a\u200b\u4f5c\u54c1\u200b\u5141\u8bb8\u200b\u7684\u200b\u8303\u56f4\u200b\uff0c\u200b\u5219\u200b\u88ab\u200b\u79f0\u4e3a\u200b \u201c\u200b\u805a\u5408\u200b\u201d\u3002\u200b\u5c06\u200b\u4e00\u4e2a\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\u5305\u542b\u200b\u5728\u200b\u4e00\u4e2a\u200b\u603b\u4f53\u200b\u4e2d\u200b\u5e76\u200b\u4e0d\u200b\u5bfc\u81f4\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u9002\u7528\u200b\u4e8e\u200b\u603b\u4f53\u200b\u7684\u200b\u5176\u4ed6\u200b\u90e8\u5206\u200b\u3002

"},{"location":"zh/about/license/#6","title":"6. \u200b\u4f20\u9012\u200b\u975e\u6e90\u200b\u5f62\u5f0f\u200b.","text":"

\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u6839\u636e\u200b\u7b2c\u200b4\u200b\u6761\u200b\u548c\u200b\u7b2c\u200b5\u200b\u6761\u200b\u7684\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u4ee5\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u5f62\u5f0f\u200b\u4f20\u9012\u200b\u88ab\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u4f46\u200b\u60a8\u200b\u4e5f\u200b\u5fc5\u987b\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u4ee5\u200b\u4e0b\u5217\u200b\u65b9\u5f0f\u200b\u4e4b\u4e00\u200b\u4f20\u9012\u200b\u673a\u5668\u200b\u53ef\u8bfb\u200b\u7684\u200b\u76f8\u5e94\u200b\u6e90\u4ee3\u7801\u200b:

a) \u200b\u5728\u200b\u5b9e\u4f53\u200b\u4ea7\u54c1\u200b\uff08\u200b\u5305\u62ec\u200b\u5b9e\u4f53\u200b\u9500\u552e\u200b\u5a92\u4ecb\u200b\uff09\u200b\u4e2d\u200b\u4f20\u9012\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\uff0c\u200b\u6216\u200b\u4f53\u73b0\u200b\u5728\u200b\u5b9e\u4f53\u200b\u4ea7\u54c1\u200b\uff08\u200b\u5305\u62ec\u200b\u5b9e\u4f53\u200b\u9500\u552e\u200b\u5a92\u4ecb\u200b\uff09\u200b\u4e2d\u200b\uff0c\u200b\u540c\u65f6\u200b\u5c06\u200b\u76f8\u5e94\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u56fa\u5b9a\u200b\u5728\u200b\u901a\u5e38\u200b\u7528\u4e8e\u200b\u8f6f\u4ef6\u200b\u4ea4\u6362\u200b\u7684\u200b\u8010\u7528\u200b\u5b9e\u4f53\u200b\u5a92\u4ecb\u200b\u4e0a\u200b\u3002 b) \u200b\u5728\u200b\u5b9e\u7269\u200b\u4ea7\u54c1\u200b\uff08\u200b\u5305\u62ec\u200b\u5b9e\u7269\u200b\u9500\u552e\u200b\u5a92\u4ecb\u200b\uff09\u200b\u4e2d\u200b\u4f20\u9012\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\uff0c\u200b\u6216\u200b\u5728\u200b\u5b9e\u7269\u200b\u4ea7\u54c1\u200b\uff08\u200b\u5305\u62ec\u200b\u5b9e\u7269\u200b\u9500\u552e\u200b\u5a92\u4ecb\u200b\uff09\u200b\u4e2d\u200b\u4f53\u73b0\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\uff0c\u200b\u5e76\u200b\u9644\u6709\u200b\u4e00\u4efd\u200b\u81f3\u5c11\u200b\u4e09\u5e74\u200b\u6709\u6548\u200b\u7684\u200b\u4e66\u9762\u200b\u62a5\u4ef7\u200b\uff0c\u200b\u53ea\u8981\u200b\u60a8\u200b\u4e3a\u200b\u8be5\u200b\u4ea7\u54c1\u578b\u53f7\u200b\u63d0\u4f9b\u200b\u5907\u4ef6\u200b\u6216\u200b\u5ba2\u6237\u200b\u652f\u6301\u200b\uff0c\u200b\u5c31\u200b\u4e00\u76f4\u200b\u6709\u6548\u200b\u3002\u200b\u5411\u200b\u4efb\u4f55\u200b\u62e5\u6709\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u4eba\u200b\u63d0\u4f9b\u200b(1)\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6240\u200b\u6db5\u76d6\u200b\u7684\u200b\u4ea7\u54c1\u200b\u4e2d\u200b\u6240\u6709\u200b\u8f6f\u4ef6\u200b\u7684\u200b\u76f8\u5e94\u200b\u6e90\u4ee3\u7801\u200b\u7684\u200b\u62f7\u8d1d\u200b\uff0c\u200b\u62f7\u8d1d\u200b\u5728\u200b\u901a\u5e38\u200b\u7528\u4e8e\u200b\u8f6f\u4ef6\u200b\u4ea4\u6362\u200b\u7684\u200b\u8010\u7528\u200b\u7269\u7406\u4ecb\u8d28\u200b\u4e0a\u200b\uff0c\u200b\u5176\u200b\u4ef7\u683c\u200b\u4e0d\u200b\u8d85\u8fc7\u200b\u8d35\u65b9\u200b\u5b9e\u9645\u200b\u6267\u884c\u200b\u8fd9\u4e00\u200b\u4f20\u9012\u200b\u6e90\u4ee3\u7801\u200b\u7684\u200b\u5408\u7406\u200b\u6210\u672c\u200b\uff0c\u200b\u6216\u8005\u200b(2)\u200b\u4ece\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\u514d\u8d39\u200b\u83b7\u53d6\u200b\u76f8\u5e94\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u62f7\u8d1d\u200b\u3002 c) \u200b\u5c06\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u5355\u4e2a\u200b\u526f\u672c\u200b\u4e0e\u200b\u63d0\u4f9b\u200b\u76f8\u5e94\u200b\u6765\u6e90\u200b\u7684\u200b\u4e66\u9762\u200b\u63d0\u8bae\u200b\u7684\u200b\u526f\u672c\u200b\u4e00\u8d77\u200b\u4f20\u9001\u200b\u3002\u200b\u53ea\u6709\u200b\u5728\u200b\u5076\u5c14\u200b\u548c\u200b\u975e\u5546\u4e1a\u6027\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u5e76\u4e14\u200b\u53ea\u6709\u200b\u5728\u200b\u60a8\u200b\u6536\u5230\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u548c\u200b\u8fd9\u79cd\u200b\u63d0\u8bae\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u624d\u200b\u5141\u8bb8\u200b\u8fd9\u79cd\u200b\u9009\u62e9\u200b\uff0c\u200b\u7b26\u5408\u200b\u7b2c\u200b6b\u200b\u6b3e\u200b\u7684\u200b\u89c4\u5b9a\u200b\u3002 d) \u200b\u901a\u8fc7\u200b\u63d0\u4f9b\u200b\u4ece\u200b\u6307\u5b9a\u200b\u5730\u70b9\u200b\uff08\u200b\u514d\u8d39\u200b\u6216\u200b\u6536\u8d39\u200b\uff09\u200b\u83b7\u53d6\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\uff0c\u200b\u5e76\u200b\u4ee5\u200b\u540c\u6837\u200b\u7684\u200b\u65b9\u5f0f\u200b\u901a\u8fc7\u200b\u540c\u4e00\u200b\u5730\u70b9\u200b\u63d0\u4f9b\u200b\u76f8\u5e94\u200b\u7684\u200b\u6e90\u7801\u200b\uff0c\u200b\u800c\u200b\u4e0d\u518d\u200b\u6536\u8d39\u200b\u3002\u200b\u60a8\u200b\u4e0d\u200b\u9700\u8981\u200b\u8981\u6c42\u200b\u63a5\u53d7\u8005\u200b\u5728\u200b\u590d\u5236\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u540c\u65f6\u200b\u590d\u5236\u200b\u76f8\u5e94\u200b\u7684\u200b\u6e90\u4ee3\u7801\u200b\u3002\u200b\u5982\u679c\u200b\u590d\u5236\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u5730\u65b9\u200b\u662f\u200b\u4e00\u4e2a\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u200b\uff0c\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u53ef\u4ee5\u200b\u5728\u200b\u53e6\u200b\u4e00\u4e2a\u200b\u652f\u6301\u200b\u540c\u7b49\u200b\u590d\u5236\u200b\u8bbe\u65bd\u200b\u7684\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\uff08\u200b\u7531\u200b\u60a8\u200b\u6216\u200b\u7b2c\u4e09\u65b9\u200b\u8fd0\u8425\u200b\uff09\uff0c\u200b\u53ea\u8981\u200b\u60a8\u200b\u5728\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u65c1\u8fb9\u200b\u4fdd\u6301\u200b\u660e\u786e\u200b\u7684\u200b\u6307\u793a\u200b\uff0c\u200b\u8bf4\u660e\u200b\u5728\u200b\u54ea\u91cc\u200b\u53ef\u4ee5\u200b\u627e\u5230\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u3002\u200b\u65e0\u8bba\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u5728\u200b\u54ea\u4e2a\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\uff0c\u200b\u60a8\u200b\u90fd\u200b\u6709\u200b\u4e49\u52a1\u200b\u786e\u4fdd\u200b\u5728\u200b\u6ee1\u8db3\u200b\u8fd9\u4e9b\u200b\u8981\u6c42\u200b\u6240\u200b\u9700\u200b\u7684\u200b\u65f6\u95f4\u200b\u5185\u200b\u63d0\u4f9b\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u3002 e) \u200b\u4f7f\u7528\u200b\u70b9\u5bf9\u70b9\u200b\u4f20\u8f93\u200b\u7684\u200b\u65b9\u5f0f\u200b\u4f20\u9001\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\uff0c\u200b\u4f46\u200b\u60a8\u200b\u5fc5\u987b\u200b\u544a\u77e5\u200b\u5176\u4ed6\u200b\u540c\u884c\u200b\uff0c\u200b\u6839\u636e\u200b\u7b2c\u200b6d\u200b\u6b3e\u200b\uff0c\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u548c\u200b\u4f5c\u54c1\u200b\u7684\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u6b63\u5728\u200b\u514d\u8d39\u200b\u63d0\u4f9b\u200b\u7ed9\u200b\u516c\u4f17\u200b\u3002 \u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u53ef\u200b\u5206\u79bb\u200b\u90e8\u5206\u200b\uff0c\u200b\u5176\u200b\u6e90\u4ee3\u7801\u200b\u4f5c\u4e3a\u200b\u7cfb\u7edf\u200b\u5e93\u200b\u88ab\u200b\u6392\u9664\u200b\u5728\u200b\u76f8\u5e94\u200b\u7684\u200b\u6e90\u7801\u200b\u4e4b\u5916\u200b\uff0c\u200b\u4e0d\u200b\u9700\u8981\u200b\u5305\u62ec\u200b\u5728\u200b\u4f20\u8fbe\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u4f5c\u54c1\u200b\u4e2d\u200b\u3002

\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b \u201c\u200b\u662f\u200b\u6307\u200b(1) \u201c\u200b\u6d88\u8d39\u54c1\u200b\u201d\uff0c\u200b\u5373\u200b\u901a\u5e38\u200b\u7528\u4e8e\u200b\u4e2a\u4eba\u200b\u3001\u200b\u5bb6\u5ead\u200b\u6216\u200b\u5bb6\u5c45\u200b\u7528\u9014\u200b\u7684\u200b\u4efb\u4f55\u200b\u6709\u5f62\u200b\u4e2a\u4eba\u8d22\u4ea7\u200b\uff0c\u200b\u6216\u200b(2)\u200b\u4e3a\u200b\u7eb3\u5165\u200b\u4f4f\u5b85\u200b\u800c\u200b\u8bbe\u8ba1\u200b\u6216\u200b\u51fa\u552e\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e1c\u897f\u200b\u3002\u200b\u5728\u200b\u786e\u5b9a\u200b\u4e00\u4e2a\u200b\u4ea7\u54c1\u200b\u662f\u5426\u662f\u200b\u6d88\u8d39\u54c1\u200b\u65f6\u200b\uff0c\u200b\u6709\u200b\u7591\u95ee\u200b\u7684\u200b\u60c5\u51b5\u200b\u5e94\u200b\u4ee5\u200b\u6709\u5229\u4e8e\u200b\u627f\u4fdd\u200b\u7684\u200b\u65b9\u5f0f\u200b\u89e3\u51b3\u200b\u3002\u200b\u5bf9\u4e8e\u200b\u7279\u5b9a\u200b\u7528\u6237\u200b\u6536\u5230\u200b\u7684\u200b\u7279\u5b9a\u200b\u4ea7\u54c1\u200b\uff0c\u201d\u200b\u901a\u5e38\u200b\u4f7f\u7528\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u8be5\u7c7b\u200b\u4ea7\u54c1\u200b\u7684\u200b\u5178\u578b\u200b\u6216\u200b\u5e38\u89c1\u200b\u7528\u9014\u200b\uff0c\u200b\u800c\u200b\u4e0d\u200b\u8003\u8651\u200b\u7279\u5b9a\u200b\u7528\u6237\u200b\u7684\u200b\u5730\u4f4d\u200b\u6216\u200b\u7279\u5b9a\u200b\u7528\u6237\u200b\u5b9e\u9645\u200b\u4f7f\u7528\u200b\u6216\u200b\u671f\u671b\u200b\u6216\u200b\u9884\u671f\u200b\u4f7f\u7528\u200b\u8be5\u200b\u4ea7\u54c1\u200b\u7684\u200b\u65b9\u5f0f\u200b\u3002\u200b\u4e00\u4e2a\u200b\u4ea7\u54c1\u200b\u662f\u200b\u6d88\u8d39\u7c7b\u200b\u4ea7\u54c1\u200b\uff0c\u200b\u65e0\u8bba\u200b\u8be5\u200b\u4ea7\u54c1\u200b\u662f\u5426\u200b\u6709\u200b\u5927\u91cf\u200b\u7684\u200b\u5546\u4e1a\u200b\u3001\u200b\u5de5\u4e1a\u200b\u6216\u975e\u200b\u6d88\u8d39\u7c7b\u200b\u7528\u9014\u200b\uff0c\u200b\u9664\u975e\u200b\u8fd9\u4e9b\u200b\u7528\u9014\u200b\u662f\u200b\u8be5\u200b\u4ea7\u54c1\u200b\u7684\u200b\u552f\u4e00\u200b\u91cd\u8981\u200b\u4f7f\u7528\u200b\u65b9\u5f0f\u200b\u3002

\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u7684\u200b \u201c\u200b\u5b89\u88c5\u200b\u4fe1\u606f\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u5728\u200b\u8be5\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u4e2d\u200b\u4ece\u200b\u5176\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u7684\u200b\u4fee\u6539\u200b\u7248\u672c\u200b\u5b89\u88c5\u200b\u548c\u200b\u6267\u884c\u200b\u6240\u200b\u6db5\u76d6\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4fee\u6539\u200b\u7248\u672c\u200b\u6240\u200b\u9700\u200b\u7684\u200b\u4efb\u4f55\u200b\u65b9\u6cd5\u200b\u3001\u200b\u7a0b\u5e8f\u200b\u3001\u200b\u6388\u6743\u200b\u5bc6\u94a5\u200b\u6216\u200b\u5176\u4ed6\u200b\u4fe1\u606f\u200b\u3002\u200b\u8fd9\u4e9b\u200b\u4fe1\u606f\u200b\u5fc5\u987b\u200b\u8db3\u4ee5\u200b\u786e\u4fdd\u200b\u5728\u200b\u4efb\u4f55\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u90fd\u200b\u4e0d\u4f1a\u200b\u4ec5\u4ec5\u200b\u56e0\u4e3a\u200b\u8fdb\u884c\u200b\u4e86\u200b\u4fee\u6539\u200b\u800c\u200b\u963b\u6b62\u200b\u6216\u200b\u5e72\u6270\u200b\u4fee\u6539\u200b\u540e\u200b\u7684\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u7ee7\u7eed\u200b\u8fd0\u884c\u200b\u3002

\u200b\u5982\u679c\u200b\u60a8\u200b\u5728\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u4e2d\u200b\uff0c\u200b\u6216\u200b\u4e0e\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u4e00\u8d77\u200b\uff0c\u200b\u6216\u200b\u4e13\u95e8\u200b\u5728\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u4e2d\u200b\u4f7f\u7528\u200b\uff0c\u200b\u5e76\u200b\u4f5c\u4e3a\u200b\u4ea4\u6613\u200b\u7684\u200b\u4e00\u90e8\u5206\u200b\uff0c\u200b\u5c06\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u7684\u200b\u62e5\u6709\u6743\u200b\u548c\u200b\u4f7f\u7528\u6743\u200b\u6c38\u4e45\u200b\u6216\u200b\u56fa\u5b9a\u200b\u5730\u200b\u8f6c\u8ba9\u200b\u7ed9\u200b\u63a5\u53d7\u8005\u200b\uff08\u200b\u65e0\u8bba\u200b\u4ea4\u6613\u200b\u5982\u4f55\u200b\u5b9a\u6027\u200b\uff09\uff0c\u200b\u6839\u636e\u200b\u672c\u6761\u200b\u89c4\u5b9a\u200b\u8f6c\u8ba9\u200b\u7684\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u5fc5\u987b\u200b\u9644\u6709\u200b\u5b89\u88c5\u200b\u4fe1\u606f\u200b\u3002\u200b\u4f46\u662f\u200b\uff0c\u200b\u5982\u679c\u200b\u60a8\u200b\u6216\u200b\u4efb\u4f55\u200b\u7b2c\u4e09\u65b9\u200b\u90fd\u200b\u6ca1\u6709\u200b\u4fdd\u7559\u200b\u5728\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u4e0a\u200b\u5b89\u88c5\u200b\u4fee\u6539\u200b\u8fc7\u200b\u7684\u200b\u76ee\u6807\u200b\u4ee3\u7801\u200b\u7684\u200b\u80fd\u529b\u200b\uff08\u200b\u4f8b\u5982\u200b\uff0c\u200b\u4f5c\u54c1\u200b\u5df2\u7ecf\u200b\u5b89\u88c5\u200b\u5728\u200bROM\u200b\u4e2d\u200b\uff09\uff0c\u200b\u5219\u200b\u8be5\u200b\u8981\u6c42\u200b\u4e0d\u200b\u9002\u7528\u200b\u3002

\u200b\u63d0\u4f9b\u200b\u5b89\u88c5\u200b\u4fe1\u606f\u200b\u7684\u200b\u8981\u6c42\u200b\u4e0d\u200b\u5305\u62ec\u200b\u7ee7\u7eed\u200b\u4e3a\u200b\u88ab\u200b\u63a5\u53d7\u8005\u200b\u4fee\u6539\u200b\u6216\u200b\u5b89\u88c5\u200b\u7684\u200b\u4f5c\u54c1\u200b\u6216\u200b\u88ab\u200b\u4fee\u6539\u200b\u6216\u200b\u5b89\u88c5\u200b\u7684\u200b\u7528\u6237\u200b\u4ea7\u54c1\u200b\u63d0\u4f9b\u200b\u652f\u6301\u200b\u670d\u52a1\u200b\u3001\u200b\u4fdd\u8bc1\u200b\u6216\u200b\u66f4\u65b0\u200b\u7684\u200b\u8981\u6c42\u200b\u3002\u200b\u5f53\u200b\u4fee\u6539\u200b\u672c\u8eab\u200b\u5bf9\u200b\u7f51\u7edc\u200b\u7684\u200b\u8fd0\u884c\u200b\u4ea7\u751f\u200b\u5b9e\u8d28\u6027\u200b\u7684\u200b\u4e0d\u5229\u200b\u5f71\u54cd\u200b\u6216\u200b\u8fdd\u53cd\u200b\u4e86\u200b\u7f51\u7edc\u200b\u4e0a\u200b\u7684\u200b\u901a\u4fe1\u200b\u89c4\u5219\u200b\u548c\u200b\u534f\u8bae\u200b\u65f6\u200b\uff0c\u200b\u53ef\u4ee5\u200b\u62d2\u7edd\u200b\u8bbf\u95ee\u200b\u7f51\u7edc\u200b\u3002 \u200b\u6839\u636e\u200b\u672c\u200b\u8282\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u6240\u200b\u4f20\u8fbe\u200b\u7684\u200b\u76f8\u5e94\u200b\u6e90\u7801\u200b\u548c\u200b\u6240\u200b\u63d0\u4f9b\u200b\u7684\u200b\u5b89\u88c5\u200b\u4fe1\u606f\u200b\u5fc5\u987b\u200b\u662f\u200b\u516c\u5f00\u200b\u8bb0\u5f55\u200b\u7684\u200b\u683c\u5f0f\u200b\uff08\u200b\u5e76\u200b\u4ee5\u200b\u6e90\u4ee3\u7801\u200b\u7684\u200b\u5f62\u5f0f\u200b\u5411\u200b\u516c\u4f17\u200b\u63d0\u4f9b\u200b\u5b9e\u73b0\u200b\uff09\uff0c\u200b\u5e76\u4e14\u200b\u5fc5\u987b\u200b\u4e0d\u200b\u9700\u8981\u200b\u7279\u6b8a\u200b\u7684\u200b\u5bc6\u7801\u200b\u6216\u200b\u94a5\u5319\u200b\u6765\u200b\u89e3\u5305\u200b\u3001\u200b\u9605\u8bfb\u200b\u6216\u200b\u590d\u5236\u200b\u3002

"},{"location":"zh/about/license/#7","title":"7. \u200b\u9644\u52a0\u200b\u6761\u6b3e\u200b.","text":"

\u201c\u200b\u9644\u52a0\u200b\u8bb8\u53ef\u200b\u201d \u200b\u662f\u200b\u5bf9\u200b\u672c\u200b\u8bb8\u53ef\u200b\u6761\u6b3e\u200b\u7684\u200b\u8865\u5145\u200b\uff0c\u200b\u5bf9\u200b\u5176\u4e2d\u200b\u7684\u200b\u4e00\u4e2a\u200b\u6216\u200b\u591a\u4e2a\u200b\u6761\u4ef6\u200b\u4f5c\u51fa\u200b\u4f8b\u5916\u200b\u89c4\u5b9a\u200b\u3002\u200b\u9002\u7528\u200b\u4e8e\u200b\u6574\u4e2a\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u9644\u52a0\u200b\u8bb8\u53ef\u200b\u5e94\u200b\u88ab\u200b\u89c6\u4e3a\u200b\u5305\u62ec\u200b\u5728\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e2d\u200b\uff0c\u200b\u53ea\u8981\u200b\u5b83\u4eec\u200b\u5728\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u4e0b\u200b\u6709\u6548\u200b\u3002\u200b\u5982\u679c\u200b\u9644\u52a0\u200b\u8bb8\u53ef\u200b\u53ea\u200b\u9002\u7528\u200b\u4e8e\u672c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u4e00\u90e8\u5206\u200b\uff0c\u200b\u5219\u200b\u8be5\u200b\u90e8\u5206\u200b\u53ef\u4ee5\u200b\u6839\u636e\u200b\u8fd9\u4e9b\u200b\u8bb8\u53ef\u200b\u5355\u72ec\u200b\u4f7f\u7528\u200b\uff0c\u200b\u4f46\u200b\u6574\u4e2a\u200b\u7a0b\u5e8f\u200b\u4ecd\u200b\u53d7\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u7ba1\u8f96\u200b\uff0c\u200b\u800c\u200b\u4e0d\u200b\u8003\u8651\u200b\u9644\u52a0\u200b\u8bb8\u53ef\u200b\u3002

\u200b\u5f53\u200b\u60a8\u200b\u8f6c\u9001\u200b\u4e00\u4efd\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u526f\u672c\u200b\u65f6\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u9009\u62e9\u200b\u4ece\u200b\u8be5\u200b\u526f\u672c\u200b\u6216\u200b\u5176\u200b\u4efb\u4f55\u200b\u90e8\u5206\u200b\u4e2d\u200b\u5220\u9664\u200b\u4efb\u4f55\u200b\u9644\u52a0\u200b\u8bb8\u53ef\u200b\u3002(\u200b\u5728\u200b\u67d0\u4e9b\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u5f53\u200b\u60a8\u200b\u4fee\u6539\u200b\u4f5c\u54c1\u200b\u65f6\u200b\uff0c\u200b\u9644\u52a0\u200b\u8bb8\u53ef\u200b\u53ef\u80fd\u200b\u88ab\u200b\u5199\u6210\u200b\u9700\u8981\u200b\u81ea\u5df1\u200b\u5220\u9664\u200b)\u3002\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u5728\u200b\u60a8\u200b\u6dfb\u52a0\u200b\u5230\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u4e2d\u200b\u7684\u200b\u6750\u6599\u200b\u4e0a\u200b\u653e\u7f6e\u200b\u989d\u5916\u200b\u7684\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u5bf9\u4e8e\u200b\u8fd9\u4e9b\u200b\u6750\u6599\u200b\uff0c\u200b\u60a8\u200b\u6709\u200b\u6216\u200b\u53ef\u4ee5\u200b\u7ed9\u4e88\u200b\u9002\u5f53\u200b\u7684\u200b\u7248\u6743\u200b\u8bb8\u53ef\u200b\u3002

\u200b\u5c3d\u7ba1\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6709\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u5bf9\u4e8e\u200b\u60a8\u200b\u6dfb\u52a0\u200b\u5230\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u4e2d\u200b\u7684\u200b\u6750\u6599\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\uff08\u200b\u5982\u679c\u200b\u5f97\u5230\u200b\u8be5\u200b\u6750\u6599\u200b\u7684\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u7684\u200b\u6388\u6743\u200b\uff09\u200b\u7528\u200b\u4ee5\u4e0b\u200b\u6761\u6b3e\u200b\u8865\u5145\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u6761\u6b3e\u200b:

a) \u200b\u4ee5\u200b\u4e0d\u540c\u4e8e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7b2c\u200b15\u200b\u6761\u200b\u548c\u200b\u7b2c\u200b16\u200b\u6761\u200b\u7684\u200b\u6761\u6b3e\u200b\u58f0\u660e\u200b\u4fdd\u8bc1\u200b\u6216\u200b\u9650\u5236\u200b\u8d23\u4efb\u200b\uff1b\u200b\u6216\u200b b) \u200b\u8981\u6c42\u200b\u5728\u200b\u8be5\u200b\u6750\u6599\u200b\u6216\u200b\u5305\u542b\u200b\u8be5\u200b\u6750\u6599\u200b\u7684\u200b\u4f5c\u54c1\u200b\u6240\u200b\u663e\u793a\u200b\u7684\u200b\u9002\u5f53\u200b\u6cd5\u5f8b\u200b\u58f0\u660e\u200b\u4e2d\u200b\u4fdd\u7559\u200b\u7279\u5b9a\u200b\u7684\u200b\u5408\u7406\u200b\u6cd5\u5f8b\u200b\u58f0\u660e\u200b\u6216\u200b\u4f5c\u8005\u200b\u5f52\u5c5e\u200b\uff1b\u200b\u6216\u200b c) \u200b\u7981\u6b62\u200b\u6b6a\u66f2\u200b\u8be5\u200b\u6750\u6599\u200b\u7684\u200b\u6765\u6e90\u200b\uff0c\u200b\u6216\u200b\u8981\u6c42\u200b\u4ee5\u200b\u5408\u7406\u200b\u7684\u200b\u65b9\u5f0f\u200b\u5c06\u200b\u8be5\u200b\u6750\u6599\u200b\u7684\u200b\u4fee\u6539\u200b\u7248\u672c\u200b\u6807\u8bb0\u200b\u4e3a\u200b\u4e0e\u200b\u539f\u59cb\u200b\u7248\u672c\u200b\u4e0d\u540c\u200b\uff1b\u200b\u6216\u200b d) \u200b\u9650\u5236\u200b\u4e3a\u200b\u5ba3\u4f20\u200b\u76ee\u7684\u200b\u4f7f\u7528\u200b\u8be5\u200b\u6750\u6599\u200b\u7684\u200b\u8bb8\u53ef\u200b\u4eba\u200b\u6216\u200b\u4f5c\u8005\u200b\u7684\u200b\u59d3\u540d\u200b\uff1b\u200b\u6216\u200b e) \u200b\u62d2\u7edd\u200b\u6839\u636e\u200b\u5546\u6807\u6cd5\u200b\u6388\u4e88\u200b\u4f7f\u7528\u200b\u67d0\u4e9b\u200b\u5546\u53f7\u200b\u3001\u200b\u5546\u6807\u200b\u6216\u200b\u670d\u52a1\u200b\u6807\u5fd7\u200b\u7684\u200b\u6743\u5229\u200b\uff1b\u200b\u6216\u200b f) \u200b\u8981\u6c42\u200b\u5c06\u200b\u6750\u6599\u200b\uff08\u200b\u6216\u200b\u6750\u6599\u200b\u7684\u200b\u4fee\u6539\u200b\u7248\u672c\u200b\uff09\u200b\u8f6c\u200b\u4ea4\u7ed9\u200b\u63a5\u53d7\u8005\u200b\u7684\u200b\u4efb\u4f55\u4eba\u200b\u5bf9\u200b\u8fd9\u4e9b\u200b\u5408\u540c\u200b\u5047\u8bbe\u200b\u76f4\u63a5\u200b\u52a0\u200b\u5728\u200b\u8fd9\u4e9b\u200b\u8bb8\u53ef\u200b\u4eba\u200b\u548c\u200b\u4f5c\u8005\u200b\u8eab\u4e0a\u200b\u7684\u200b\u4efb\u4f55\u200b\u8d23\u4efb\u200b\u8fdb\u884c\u200b\u8d54\u507f\u200b\u3002 \u200b\u6240\u6709\u200b\u5176\u4ed6\u200b\u975e\u200b\u8bb8\u53ef\u200b\u6027\u200b\u7684\u200b\u9644\u52a0\u200b\u6761\u6b3e\u200b\u90fd\u200b\u88ab\u200b\u89c6\u4e3a\u200b\u7b2c\u200b10\u200b\u6761\u200b\u610f\u4e49\u200b\u4e0a\u200b\u7684\u200b \u201c\u200b\u8fdb\u4e00\u6b65\u200b\u9650\u5236\u200b\u201d\u3002\u200b\u5982\u679c\u200b\u60a8\u200b\u6536\u5230\u200b\u7684\u200b\u7a0b\u5e8f\u200b\u6216\u200b\u5176\u200b\u4efb\u4f55\u200b\u90e8\u5206\u200b\u5305\u542b\u200b\u4e00\u4e2a\u200b\u901a\u77e5\u200b\uff0c\u200b\u8bf4\u660e\u200b\u5b83\u200b\u53d7\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u7ba1\u8f96\u200b\uff0c\u200b\u540c\u65f6\u200b\u8fd8\u6709\u200b\u4e00\u4e2a\u200b\u5c5e\u4e8e\u200b\u8fdb\u4e00\u6b65\u200b\u9650\u5236\u200b\u7684\u200b\u6761\u6b3e\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u5220\u9664\u200b\u8be5\u200b\u6761\u6b3e\u200b\u3002\u200b\u5982\u679c\u200b\u8bb8\u53ef\u200b\u6587\u4ef6\u200b\u5305\u542b\u200b\u8fdb\u4e00\u6b65\u200b\u7684\u200b\u9650\u5236\u200b\uff0c\u200b\u4f46\u200b\u5141\u8bb8\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u8fdb\u884c\u200b\u518d\u200b\u8bb8\u53ef\u200b\u6216\u200b\u8f6c\u8ba9\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u5728\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\u4e2d\u200b\u6dfb\u52a0\u200b\u53d7\u8be5\u200b\u8bb8\u53ef\u200b\u6587\u4ef6\u200b\u6761\u6b3e\u200b\u7ba1\u8f96\u200b\u7684\u200b\u6750\u6599\u200b\uff0c\u200b\u4f46\u200b\u8fdb\u4e00\u6b65\u200b\u7684\u200b\u9650\u5236\u200b\u5728\u200b\u8fd9\u79cd\u200b\u518d\u200b\u8bb8\u53ef\u200b\u6216\u200b\u8f6c\u8ba9\u200b\u4e2d\u200b\u4e0d\u200b\u5b58\u5728\u200b\u3002

\u200b\u5982\u679c\u200b\u60a8\u200b\u6309\u7167\u200b\u672c\u8282\u200b\u7684\u200b\u89c4\u5b9a\u200b\u5411\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\u6dfb\u52a0\u200b\u6761\u6b3e\u200b\uff0c\u200b\u60a8\u200b\u5fc5\u987b\u200b\u5728\u200b\u76f8\u5173\u200b\u7684\u200b\u6e90\u6587\u4ef6\u200b\u4e2d\u200b\u58f0\u660e\u200b\u9002\u7528\u200b\u4e8e\u200b\u8fd9\u4e9b\u200b\u6587\u4ef6\u200b\u7684\u200b\u9644\u52a0\u200b\u6761\u6b3e\u200b\uff0c\u200b\u6216\u8005\u200b\u8bf4\u660e\u200b\u5728\u200b\u54ea\u91cc\u200b\u53ef\u4ee5\u200b\u627e\u5230\u200b\u9002\u7528\u200b\u6761\u6b3e\u200b\u3002

\u200b\u989d\u5916\u200b\u7684\u200b\u6761\u6b3e\u200b\uff0c\u200b\u4e0d\u7ba1\u200b\u662f\u200b\u5141\u8bb8\u200b\u7684\u200b\u8fd8\u200b\u662f\u975e\u200b\u5141\u8bb8\u200b\u7684\u200b\uff0c\u200b\u90fd\u200b\u53ef\u4ee5\u200b\u4ee5\u200b\u5355\u72ec\u200b\u7684\u200b\u4e66\u9762\u200b\u8bb8\u53ef\u200b\u7684\u200b\u5f62\u5f0f\u200b\u8bf4\u660e\u200b\uff0c\u200b\u6216\u8005\u200b\u4f5c\u4e3a\u200b\u4f8b\u5916\u60c5\u51b5\u200b\u8bf4\u660e\u200b\uff1b\u200b\u4e0a\u8ff0\u200b\u8981\u6c42\u200b\u9002\u7528\u200b\u4e8e\u200b\u4efb\u4f55\u200b\u4e00\u79cd\u200b\u65b9\u5f0f\u200b\u3002

"},{"location":"zh/about/license/#8","title":"8. \u200b\u7ec8\u6b62\u200b.","text":"

\u200b\u9664\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u660e\u786e\u89c4\u5b9a\u200b\u7684\u200b\u60c5\u51b5\u200b\u5916\u200b\uff0c\u200b\u60a8\u200b\u4e0d\u5f97\u200b\u4f20\u64ad\u200b\u6216\u200b\u4fee\u6539\u200b\u8986\u76d6\u200b\u4f5c\u54c1\u200b\u3002\u200b\u4efb\u4f55\u200b\u4ee5\u200b\u5176\u4ed6\u200b\u65b9\u5f0f\u200b\u4f20\u64ad\u200b\u6216\u200b\u4fee\u6539\u200b\u4f5c\u54c1\u200b\u7684\u200b\u5c1d\u8bd5\u200b\u90fd\u200b\u662f\u200b\u65e0\u6548\u200b\u7684\u200b\uff0c\u200b\u5e76\u200b\u5c06\u200b\u81ea\u52a8\u200b\u7ec8\u6b62\u200b\u60a8\u200b\u5728\u200b\u672c\u200b\u8bb8\u53ef\u200b\u4e0b\u200b\u7684\u200b\u6743\u5229\u200b\uff08\u200b\u5305\u62ec\u200b\u6839\u636e\u200b\u7b2c\u200b11\u200b\u8282\u200b\u7b2c\u4e09\u6bb5\u200b\u6388\u4e88\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\uff09\u3002

\u200b\u7136\u800c\u200b\uff0c\u200b\u5982\u679c\u200b\u60a8\u200b\u505c\u6b62\u200b\u6240\u6709\u200b\u8fdd\u53cd\u200b\u672c\u200b\u8bb8\u53ef\u200b\u7684\u200b\u884c\u4e3a\u200b\uff0c\u200b\u90a3\u4e48\u200b\u60a8\u200b\u4ece\u200b\u67d0\u200b\u4e00\u200b\u7279\u5b9a\u200b\u7248\u6743\u200b\u4eba\u5904\u200b\u83b7\u5f97\u200b\u7684\u200b\u8bb8\u53ef\u200b\u5c06\u200b\u88ab\u200b\u6062\u590d\u200b\uff08a\uff09\u200b\u6682\u65f6\u6027\u200b\u7684\u200b\uff0c\u200b\u9664\u975e\u200b\u5e76\u200b\u76f4\u5230\u200b\u7248\u6743\u200b\u4eba\u200b\u660e\u786e\u200b\u5e76\u200b\u6700\u7ec8\u200b\u7ec8\u6b62\u200b\u60a8\u200b\u7684\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u4ee5\u53ca\u200b\uff08b\uff09\u200b\u6c38\u4e45\u6027\u200b\u7684\u200b\uff0c\u200b\u5982\u679c\u200b\u7248\u6743\u200b\u4eba\u200b\u672a\u80fd\u200b\u5728\u200b\u505c\u6b62\u200b\u540e\u200b\u7684\u200b60\u200b\u5929\u200b\u5185\u200b\u901a\u8fc7\u200b\u67d0\u79cd\u200b\u5408\u7406\u200b\u7684\u200b\u65b9\u5f0f\u200b\u901a\u77e5\u200b\u60a8\u200b\u4fb5\u6743\u884c\u4e3a\u200b\u3002

\u200b\u6b64\u5916\u200b\uff0c\u200b\u5982\u679c\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u4ee5\u200b\u67d0\u79cd\u200b\u5408\u7406\u200b\u7684\u200b\u65b9\u5f0f\u200b\u901a\u77e5\u200b\u60a8\u200b\u4fb5\u6743\u884c\u4e3a\u200b\uff0c\u200b\u8fd9\u200b\u662f\u200b\u60a8\u200b\u7b2c\u4e00\u6b21\u200b\u6536\u5230\u200b\u8be5\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u8fdd\u53cd\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u901a\u77e5\u200b\uff08\u200b\u9488\u5bf9\u200b\u4efb\u4f55\u200b\u4f5c\u54c1\u200b\uff09\uff0c\u200b\u5e76\u4e14\u200b\u60a8\u200b\u5728\u200b\u6536\u5230\u200b\u901a\u77e5\u200b\u540e\u200b30\u200b\u5929\u200b\u5185\u200b\u7ea0\u6b63\u200b\u4e86\u200b\u4fb5\u6743\u884c\u4e3a\u200b\uff0c\u200b\u90a3\u4e48\u200b\u60a8\u200b\u4ece\u200b\u67d0\u200b\u4e00\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u83b7\u5f97\u200b\u7684\u200b\u8bb8\u53ef\u8bc1\u200b\u5c06\u200b\u88ab\u200b\u6c38\u4e45\u200b\u6062\u590d\u200b\u3002

\u200b\u7ec8\u6b62\u200b\u60a8\u200b\u5728\u200b\u672c\u8282\u200b\u4e0b\u200b\u7684\u200b\u6743\u5229\u200b\u5e76\u200b\u4e0d\u200b\u7ec8\u6b62\u200b\u90a3\u4e9b\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4ece\u200b\u60a8\u200b\u90a3\u91cc\u200b\u83b7\u5f97\u200b\u526f\u672c\u200b\u6216\u200b\u6743\u5229\u200b\u7684\u200b\u5404\u65b9\u200b\u7684\u200b\u8bb8\u53ef\u200b\u3002\u200b\u5982\u679c\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b\u5df2\u7ecf\u200b\u88ab\u200b\u7ec8\u6b62\u200b\uff0c\u200b\u800c\u4e14\u200b\u6ca1\u6709\u200b\u6c38\u4e45\u200b\u6062\u590d\u200b\uff0c\u200b\u60a8\u200b\u5c31\u200b\u6ca1\u6709\u200b\u8d44\u683c\u200b\u6839\u636e\u200b\u7b2c\u200b10\u200b\u6761\u200b\u83b7\u5f97\u200b\u76f8\u540c\u200b\u6750\u6599\u200b\u7684\u200b\u65b0\u200b\u8bb8\u53ef\u200b\u3002

"},{"location":"zh/about/license/#9","title":"9. \u200b\u62e5\u6709\u200b\u526f\u672c\u200b\u4e0d\u200b\u9700\u8981\u200b\u63a5\u53d7\u200b.","text":"

\u200b\u60a8\u200b\u4e0d\u200b\u9700\u8981\u200b\u4e3a\u4e86\u200b\u63a5\u6536\u200b\u6216\u200b\u8fd0\u884c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u526f\u672c\u200b\u800c\u200b\u63a5\u53d7\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u3002\u200b\u4ec5\u4ec5\u200b\u7531\u4e8e\u200b\u4f7f\u7528\u200b\u70b9\u5bf9\u70b9\u200b\u4f20\u8f93\u200b\u6765\u200b\u63a5\u6536\u200b\u62f7\u8d1d\u200b\u800c\u200b\u53d1\u751f\u200b\u7684\u200b\u88ab\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u8f85\u52a9\u200b\u4f20\u64ad\u200b\uff0c\u200b\u4e5f\u200b\u540c\u6837\u200b\u4e0d\u200b\u9700\u8981\u200b\u63a5\u53d7\u200b\u3002\u200b\u7136\u800c\u200b\uff0c\u200b\u9664\u4e86\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e4b\u5916\u200b\uff0c\u200b\u6ca1\u6709\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u4e1c\u897f\u200b\u5141\u8bb8\u200b\u60a8\u200b\u4f20\u64ad\u200b\u6216\u200b\u4fee\u6539\u200b\u4efb\u4f55\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\u3002\u200b\u5982\u679c\u200b\u60a8\u200b\u4e0d\u200b\u63a5\u53d7\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u884c\u4e3a\u200b\u5c31\u200b\u4fb5\u72af\u200b\u4e86\u200b\u7248\u6743\u200b\u3002\u200b\u56e0\u6b64\u200b\uff0c\u200b\u901a\u8fc7\u200b\u4fee\u6539\u200b\u6216\u200b\u4f20\u64ad\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u60a8\u200b\u8868\u660e\u200b\u60a8\u200b\u63a5\u53d7\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u53ef\u4ee5\u200b\u8fd9\u6837\u200b\u505a\u200b\u3002

"},{"location":"zh/about/license/#10","title":"10. \u200b\u4e0b\u6e38\u200b\u63a5\u53d7\u8005\u200b\u7684\u200b\u81ea\u52a8\u200b\u8bb8\u53ef\u200b.","text":"

\u200b\u6bcf\u5f53\u200b\u60a8\u200b\u4f20\u9012\u200b\u4e00\u4e2a\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\u65f6\u200b\uff0c\u200b\u63a5\u6536\u8005\u200b\u4f1a\u200b\u81ea\u52a8\u200b\u4ece\u200b\u539f\u59cb\u200b\u8bb8\u53ef\u200b\u4eba\u200b\u90a3\u91cc\u200b\u5f97\u5230\u200b\u4e00\u4e2a\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u5728\u200b\u9075\u5b88\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u524d\u63d0\u200b\u4e0b\u200b\uff0c\u200b\u8fd0\u884c\u200b\u3001\u200b\u4fee\u6539\u200b\u548c\u200b\u4f20\u64ad\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u3002\u200b\u60a8\u200b\u4e0d\u200b\u8d1f\u8d23\u200b\u6267\u884c\u200b\u7b2c\u4e09\u65b9\u200b\u5bf9\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u9075\u5b88\u200b\u3002

\u200b\u5b9e\u4f53\u200b\u4ea4\u6613\u200b \u201c\u200b\u662f\u200b\u6307\u200b\u8f6c\u8ba9\u200b\u4e00\u4e2a\u200b\u7ec4\u7ec7\u200b\u7684\u200b\u63a7\u5236\u6743\u200b\uff0c\u200b\u6216\u200b\u4e00\u4e2a\u200b\u7ec4\u7ec7\u200b\u7684\u200b\u5927\u90e8\u5206\u200b\u8d44\u4ea7\u200b\uff0c\u200b\u6216\u200b\u62c6\u5206\u200b\u4e00\u4e2a\u200b\u7ec4\u7ec7\u200b\uff0c\u200b\u6216\u200b\u5408\u5e76\u200b\u7ec4\u7ec7\u200b\u7684\u200b\u4ea4\u6613\u200b\u3002\u200b\u5982\u679c\u200b\u5b9e\u4f53\u200b\u4ea4\u6613\u200b\u5bfc\u81f4\u200b\u8986\u76d6\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4f20\u64ad\u200b\uff0c\u200b\u90a3\u4e48\u200b\u6536\u5230\u200b\u4f5c\u54c1\u200b\u526f\u672c\u200b\u7684\u200b\u6bcf\u200b\u4e00\u4e2a\u200b\u4ea4\u6613\u200b\u65b9\u200b\u4e5f\u200b\u4f1a\u200b\u6536\u5230\u200b\u8be5\u65b9\u200b\u7684\u200b\u6743\u76ca\u200b\u524d\u8eab\u200b\u6839\u636e\u200b\u524d\u6bb5\u200b\u89c4\u5b9a\u200b\u6240\u200b\u62e5\u6709\u200b\u6216\u200b\u53ef\u4ee5\u200b\u7ed9\u4e88\u200b\u7684\u200b\u4efb\u4f55\u200b\u4f5c\u54c1\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u4ece\u200b\u6743\u76ca\u200b\u524d\u200b\u8eab\u5904\u200b\u83b7\u5f97\u200b\u4f5c\u54c1\u200b\u5bf9\u5e94\u200b\u6e90\u200b\u7684\u200b\u62e5\u6709\u6743\u200b\uff0c\u200b\u5982\u679c\u200b\u6743\u76ca\u200b\u524d\u8eab\u200b\u62e5\u6709\u200b\u6216\u200b\u901a\u8fc7\u200b\u5408\u7406\u200b\u52aa\u529b\u200b\u53ef\u4ee5\u200b\u83b7\u5f97\u200b\u3002

\u200b\u60a8\u200b\u4e0d\u5f97\u200b\u5bf9\u200b\u884c\u4f7f\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e0b\u200b\u6388\u4e88\u200b\u6216\u200b\u786e\u8ba4\u200b\u7684\u200b\u6743\u5229\u200b\u65bd\u52a0\u200b\u4efb\u4f55\u200b\u8fdb\u4e00\u6b65\u200b\u7684\u200b\u9650\u5236\u200b\u3002\u200b\u4f8b\u5982\u200b\uff0c\u200b\u60a8\u200b\u4e0d\u5f97\u200b\u5bf9\u200b\u884c\u4f7f\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6240\u200b\u6388\u4e88\u200b\u7684\u200b\u6743\u5229\u200b\u5f81\u6536\u200b\u8bb8\u53ef\u8d39\u200b\u3001\u200b\u7279\u8bb8\u6743\u200b\u4f7f\u7528\u8d39\u200b\u6216\u200b\u5176\u4ed6\u8d39\u7528\u200b\uff0c\u200b\u4e5f\u200b\u4e0d\u5f97\u200b\u63d0\u8d77\u200b\u8bc9\u8bbc\u200b\uff08\u200b\u5305\u62ec\u200b\u8bc9\u8bbc\u200b\u4e2d\u200b\u7684\u200b\u4ea4\u53c9\u200b\u7d22\u8d54\u200b\u6216\u200b\u53cd\u200b\u7d22\u8d54\u200b\uff09\uff0c\u200b\u6307\u63a7\u200b\u5236\u4f5c\u200b\u3001\u200b\u4f7f\u7528\u200b\u3001\u200b\u9500\u552e\u200b\u3001\u200b\u63d0\u4f9b\u200b\u9500\u552e\u200b\u6216\u200b\u8fdb\u53e3\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u6216\u200b\u5176\u200b\u4efb\u4f55\u200b\u90e8\u5206\u200b\u4fb5\u72af\u200b\u4e86\u200b\u4efb\u4f55\u200b\u4e13\u5229\u200b\u6743\u5229\u200b\u3002

"},{"location":"zh/about/license/#11","title":"11. \u200b\u4e13\u5229\u200b.","text":"

\u200b\u8d21\u732e\u8005\u200b \u201c\u200b\u662f\u200b\u6307\u200b\u6388\u6743\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4f7f\u7528\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u6216\u672c\u200b\u7a0b\u5e8f\u200b\u6240\u200b\u57fa\u4e8e\u200b\u7684\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u3002\u200b\u8fd9\u6837\u200b\u6388\u6743\u200b\u7684\u200b\u4f5c\u54c1\u200b\u88ab\u200b\u79f0\u4e3a\u200b\u8d21\u732e\u8005\u200b\u7684\u200b \u201c\u200b\u8d21\u732e\u8005\u200b\u7248\u672c\u200b\u201d\u3002

\u200b\u8d21\u732e\u8005\u200b\u7684\u200b \u201c\u200b\u57fa\u672c\u200b\u4e13\u5229\u200b\u6743\u5229\u200b\u8981\u6c42\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u8d21\u732e\u8005\u200b\u62e5\u6709\u200b\u6216\u200b\u63a7\u5236\u200b\u7684\u200b\u6240\u6709\u200b\u4e13\u5229\u200b\u6743\u5229\u200b\u8981\u6c42\u200b\uff0c\u200b\u4e0d\u7ba1\u200b\u662f\u200b\u5df2\u7ecf\u200b\u83b7\u5f97\u200b\u7684\u200b\u8fd8\u662f\u200b\u4ee5\u540e\u200b\u83b7\u5f97\u200b\u7684\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u4e13\u5229\u200b\u6743\u5229\u200b\u8981\u6c42\u200b\u5c06\u200b\u88ab\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u5141\u8bb8\u200b\u7684\u200b\u67d0\u79cd\u200b\u65b9\u5f0f\u200b\u6240\u200b\u4fb5\u72af\u200b\uff0c\u200b\u5373\u200b\u5236\u4f5c\u200b\u3001\u200b\u4f7f\u7528\u200b\u6216\u200b\u9500\u552e\u200b\u5176\u200b\u8d21\u732e\u8005\u200b\u7248\u672c\u200b\uff0c\u200b\u4f46\u200b\u4e0d\u200b\u5305\u62ec\u200b\u4ec5\u200b\u56e0\u200b\u8fdb\u4e00\u6b65\u200b\u4fee\u6539\u200b\u8d21\u732e\u8005\u200b\u7248\u672c\u200b\u800c\u200b\u88ab\u200b\u4fb5\u72af\u200b\u7684\u200b\u6743\u5229\u200b\u8981\u6c42\u200b\u3002\u200b\u5c31\u200b\u672c\u200b\u5b9a\u4e49\u200b\u800c\u8a00\u200b\uff0c\u201d\u200b\u63a7\u5236\u200b\u201d \u200b\u5305\u62ec\u200b\u4ee5\u200b\u7b26\u5408\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u8981\u6c42\u200b\u7684\u200b\u65b9\u5f0f\u200b\u6388\u4e88\u200b\u4e13\u5229\u200b\u5206\u200b\u8bb8\u53ef\u200b\u7684\u200b\u6743\u5229\u200b\u3002

\u200b\u6bcf\u4e2a\u200b\u8d21\u732e\u8005\u200b\u6839\u636e\u200b\u8d21\u732e\u8005\u200b\u7684\u200b\u57fa\u672c\u200b\u4e13\u5229\u200b\u6743\u5229\u200b\u8981\u6c42\u200b\uff0c\u200b\u6388\u4e88\u200b\u60a8\u200b\u975e\u200b\u72ec\u5360\u6027\u200b\u7684\u200b\u3001\u200b\u5168\u7403\u6027\u200b\u7684\u200b\u3001\u200b\u514d\u200b\u7248\u7a0e\u200b\u7684\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u4ee5\u200b\u5236\u9020\u200b\u3001\u200b\u4f7f\u7528\u200b\u3001\u200b\u9500\u552e\u200b\u3001\u200b\u63d0\u4f9b\u200b\u9500\u552e\u200b\u3001\u200b\u8fdb\u53e3\u200b\u548c\u200b\u4ee5\u200b\u5176\u4ed6\u200b\u65b9\u5f0f\u200b\u8fd0\u884c\u200b\u3001\u200b\u4fee\u6539\u200b\u548c\u200b\u4f20\u64ad\u200b\u5176\u200b\u8d21\u732e\u8005\u200b\u7248\u672c\u200b\u7684\u200b\u5185\u5bb9\u200b\u3002

\u200b\u5728\u200b\u4ee5\u4e0b\u200b\u4e09\u6bb5\u200b\u4e2d\u200b\uff0c\u201d\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u4e0d\u200b\u5b9e\u65bd\u200b\u4e13\u5229\u200b\u7684\u200b\u4efb\u4f55\u200b\u660e\u793a\u200b\u534f\u8bae\u200b\u6216\u200b\u627f\u8bfa\u200b\uff0c\u200b\u65e0\u8bba\u200b\u5176\u200b\u540d\u79f0\u200b\u5982\u4f55\u200b\uff08\u200b\u4f8b\u5982\u200b\uff0c\u200b\u660e\u786e\u200b\u5141\u8bb8\u200b\u5b9e\u65bd\u200b\u4e13\u5229\u200b\u6216\u200b\u4e0d\u200b\u8d77\u8bc9\u200b\u4e13\u5229\u200b\u4fb5\u6743\u200b\u7684\u200b\u7ea6\u5b9a\u200b\uff09\u3002\u200b\u5411\u200b\u4e00\u65b9\u200b \u201c\u200b\u6388\u4e88\u200b\u201d \u200b\u8fd9\u79cd\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u610f\u5473\u7740\u200b\u4f5c\u51fa\u200b\u8fd9\u79cd\u200b\u534f\u8bae\u200b\u6216\u200b\u627f\u8bfa\u200b\uff0c\u200b\u4e0d\u200b\u5bf9\u200b\u8be5\u65b9\u200b\u5b9e\u65bd\u200b\u4e13\u5229\u200b\u3002

\u200b\u5982\u679c\u200b\u60a8\u200b\u5728\u200b\u77e5\u60c5\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u4f9d\u9760\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u4f20\u9012\u200b\u4e86\u200b\u4e00\u4e2a\u200b\u6db5\u76d6\u200b\u7684\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u800c\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u7684\u200b\u76f8\u5e94\u200b\u6765\u6e90\u200b\u5e76\u200b\u6ca1\u6709\u200b\u901a\u8fc7\u200b\u516c\u5f00\u200b\u7684\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u200b\u6216\u200b\u5176\u4ed6\u200b\u5bb9\u6613\u200b\u83b7\u5f97\u200b\u7684\u200b\u65b9\u5f0f\u200b\uff0c\u200b\u4f9b\u200b\u4efb\u4f55\u4eba\u200b\u6839\u636e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u6761\u6b3e\u200b\u514d\u8d39\u200b\u590d\u5236\u200b\uff0c\u200b\u90a3\u4e48\u200b\u60a8\u200b\u5fc5\u987b\u200b(1)\u200b\u4f7f\u200b\u76f8\u5e94\u200b\u6765\u6e90\u200b\u53ef\u4ee5\u200b\u83b7\u5f97\u200b\uff0c\u200b\u6216\u8005\u200b(2)\u200b\u5b89\u6392\u200b\u5265\u593a\u200b\u81ea\u5df1\u200b\u5bf9\u200b\u8be5\u200b\u7279\u5b9a\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u5229\u76ca\u200b\uff0c\u200b\u6216\u8005\u200b(3)\u200b\u4ee5\u200b\u7b26\u5408\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u8981\u6c42\u200b\u7684\u200b\u65b9\u5f0f\u200b\uff0c\u200b\u5b89\u6392\u200b\u5c06\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u6269\u5c55\u200b\u5230\u200b\u4e0b\u6e38\u200b\u63a5\u53d7\u8005\u200b\u3002\u201d\u200b\u660e\u77e5\u6545\u72af\u200b\u201d \u200b\u662f\u200b\u6307\u200b\u60a8\u200b\u5b9e\u9645\u200b\u77e5\u9053\u200b\uff0c\u200b\u5982\u679c\u200b\u6ca1\u6709\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u60a8\u200b\u5728\u200b\u67d0\u4e2a\u200b\u56fd\u5bb6\u200b\u4f20\u9012\u200b\u6240\u6d89\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u6216\u8005\u200b\u60a8\u200b\u7684\u200b\u63a5\u53d7\u8005\u200b\u5728\u200b\u67d0\u4e2a\u200b\u56fd\u5bb6\u200b\u4f7f\u7528\u200b\u6240\u6d89\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u5c06\u200b\u4fb5\u72af\u200b\u60a8\u200b\u6709\u200b\u7406\u7531\u200b\u76f8\u4fe1\u200b\u5728\u200b\u8be5\u56fd\u200b\u6709\u6548\u200b\u7684\u200b\u4e00\u9879\u200b\u6216\u200b\u591a\u9879\u200b\u53ef\u200b\u8bc6\u522b\u200b\u4e13\u5229\u200b\u3002

\u200b\u5982\u679c\u200b\u6839\u636e\u200b\u4e00\u9879\u200b\u4ea4\u6613\u200b\u6216\u200b\u5b89\u6392\u200b\u6216\u200b\u4e0e\u200b\u4e4b\u200b\u76f8\u5173\u200b\uff0c\u200b\u60a8\u200b\u8f6c\u8ba9\u200b\u6216\u200b\u901a\u8fc7\u200b\u4fc3\u6210\u200b\u8f6c\u8ba9\u200b\u4f20\u64ad\u200b\u88ab\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u5e76\u200b\u5411\u200b\u63a5\u53d7\u200b\u88ab\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u67d0\u4e9b\u200b\u5f53\u4e8b\u65b9\u200b\u6388\u4e88\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\uff0c\u200b\u6388\u6743\u200b\u4ed6\u4eec\u200b\u4f7f\u7528\u200b\u3001\u200b\u4f20\u64ad\u200b\u3001\u200b\u4fee\u6539\u200b\u6216\u200b\u8f6c\u8ba9\u200b\u88ab\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7279\u5b9a\u200b\u526f\u672c\u200b\uff0c\u200b\u90a3\u4e48\u200b\u60a8\u200b\u6388\u4e88\u200b\u7684\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u5c06\u200b\u81ea\u52a8\u200b\u6269\u5c55\u200b\u5230\u200b\u88ab\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u548c\u200b\u57fa\u4e8e\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u7684\u200b\u6240\u6709\u200b\u63a5\u53d7\u8005\u200b\u3002

\u200b\u5982\u679c\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u4e0d\u200b\u5305\u62ec\u200b\u5728\u200b\u5176\u200b\u8986\u76d6\u8303\u56f4\u200b\u5185\u200b\uff0c\u200b\u7981\u6b62\u200b\u884c\u4f7f\u200b\u6216\u200b\u4ee5\u200b\u4e0d\u200b\u884c\u4f7f\u200b\u672c\u200b\u8bb8\u53ef\u200b\u5177\u4f53\u200b\u6388\u4e88\u200b\u7684\u200b\u4e00\u9879\u200b\u6216\u200b\u591a\u9879\u200b\u6743\u5229\u200b\u4e3a\u200b\u6761\u4ef6\u200b\uff0c\u200b\u5219\u200b\u4e3a\u200b \u201c\u200b\u6b67\u89c6\u6027\u200b\u7684\u200b\u201d\u3002\u200b\u5982\u679c\u200b\u60a8\u200b\u662f\u200b\u4e0e\u200b\u4ece\u4e8b\u200b\u8f6f\u4ef6\u200b\u5206\u9500\u200b\u4e1a\u52a1\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u8fbe\u6210\u200b\u7684\u200b\u5b89\u6392\u200b\u7684\u200b\u4e00\u65b9\u200b\uff0c\u200b\u6839\u636e\u200b\u8be5\u200b\u5b89\u6392\u200b\uff0c\u200b\u60a8\u200b\u6839\u636e\u200b\u60a8\u200b\u4f20\u9012\u200b\u4f5c\u54c1\u200b\u7684\u200b\u6d3b\u52a8\u200b\u8303\u56f4\u200b\u5411\u200b\u7b2c\u4e09\u65b9\u200b\u4ed8\u6b3e\u200b\uff0c\u200b\u5e76\u4e14\u200b\u6839\u636e\u200b\u8be5\u200b\u5b89\u6392\u200b\uff0c\u200b\u7b2c\u4e09\u200b\u65b9\u5411\u200b\u4efb\u4f55\u200b\u5c06\u200b\u4ece\u200b\u60a8\u200b\u90a3\u91cc\u200b\u83b7\u5f97\u200b\u6240\u6d89\u200b\u4f5c\u54c1\u200b\u7684\u200b\u4e00\u65b9\u200b\u6388\u4e88\u200b\uff0c\u200b\u5219\u200b\u60a8\u200b\u4e0d\u5f97\u200b\u4f20\u9012\u200b\u6240\u6d89\u200b\u4f5c\u54c1\u200b\u3002\u200b\u6b67\u89c6\u6027\u200b\u7684\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\uff1a(a)\u200b\u4e0e\u200b\u60a8\u200b\u4f20\u9012\u200b\u7684\u200b\u4f5c\u54c1\u200b\u7684\u200b\u526f\u672c\u200b\uff08\u200b\u6216\u200b\u7531\u200b\u8fd9\u4e9b\u200b\u526f\u672c\u200b\u5236\u4f5c\u200b\u7684\u200b\u526f\u672c\u200b\uff09\u200b\u6709\u5173\u200b\uff0c\u200b\u6216\u200b(b)\u200b\u4e3b\u8981\u200b\u4e3a\u200b\u5305\u542b\u200b\u8be5\u200b\u4f5c\u54c1\u200b\u7684\u200b\u7279\u5b9a\u200b\u4ea7\u54c1\u200b\u6216\u200b\u6c47\u7f16\u200b\u5e76\u200b\u4e0e\u200b\u4e4b\u200b\u6709\u5173\u200b\uff0c\u200b\u9664\u975e\u200b\u60a8\u200b\u5728\u200b2007\u200b\u5e74\u200b3\u200b\u6708\u200b28\u200b\u65e5\u200b\u4e4b\u524d\u200b\u8fbe\u6210\u200b\u8be5\u200b\u5b89\u6392\u200b\uff0c\u200b\u6216\u200b\u6388\u4e88\u200b\u8be5\u200b\u4e13\u5229\u200b\u8bb8\u53ef\u200b\u3002

\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e2d\u200b\u7684\u200b\u4efb\u4f55\u200b\u5185\u5bb9\u200b\u90fd\u200b\u4e0d\u5e94\u200b\u88ab\u200b\u89e3\u91ca\u200b\u4e3a\u200b\u6392\u9664\u200b\u6216\u200b\u9650\u5236\u200b\u4efb\u4f55\u200b\u9690\u542b\u200b\u7684\u200b\u8bb8\u53ef\u200b\u6216\u200b\u5176\u4ed6\u200b\u5bf9\u200b\u4fb5\u6743\u200b\u7684\u200b\u6297\u8fa9\u200b\uff0c\u200b\u6839\u636e\u200b\u9002\u7528\u200b\u7684\u200b\u4e13\u5229\u6cd5\u200b\uff0c\u200b\u60a8\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u6709\u200b\u8fd9\u6837\u200b\u7684\u200b\u673a\u4f1a\u200b\u3002

"},{"location":"zh/about/license/#12","title":"12. \u200b\u4e0d\u200b\u653e\u5f03\u200b\u4ed6\u4eba\u200b\u7684\u200b\u81ea\u7531\u200b.","text":"

\u200b\u5982\u679c\u200b\u5f3a\u52a0\u200b\u7ed9\u200b\u60a8\u200b\u7684\u200b\u6761\u4ef6\u200b\uff08\u200b\u65e0\u8bba\u662f\u200b\u901a\u8fc7\u200b\u6cd5\u9662\u200b\u547d\u4ee4\u200b\u3001\u200b\u534f\u8bae\u200b\u6216\u200b\u5176\u4ed6\u200b\u65b9\u5f0f\u200b\uff09\u200b\u4e0e\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u6761\u4ef6\u200b\u76f8\u200b\u62b5\u89e6\u200b\uff0c\u200b\u5b83\u4eec\u200b\u5e76\u200b\u4e0d\u80fd\u200b\u514d\u9664\u200b\u60a8\u200b\u5bf9\u200b\u672c\u200b\u8bb8\u53ef\u200b\u6761\u4ef6\u200b\u7684\u200b\u9075\u5b88\u200b\u3002\u200b\u5982\u679c\u200b\u60a8\u200b\u4e0d\u80fd\u200b\u5728\u200b\u8f6c\u8ba9\u200b\u4f5c\u54c1\u200b\u65f6\u200b\u540c\u65f6\u200b\u6ee1\u8db3\u200b\u60a8\u200b\u5728\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u4e0b\u200b\u7684\u200b\u4e49\u52a1\u200b\u548c\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u76f8\u5173\u200b\u7684\u200b\u4e49\u52a1\u200b\uff0c\u200b\u90a3\u4e48\u200b\u4f5c\u4e3a\u200b\u7ed3\u679c\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u4e0d\u200b\u8f6c\u8ba9\u200b\u5b83\u200b\u3002\u200b\u4f8b\u5982\u200b\uff0c\u200b\u5982\u679c\u200b\u60a8\u200b\u540c\u610f\u200b\u7684\u200b\u6761\u6b3e\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u60a8\u200b\u6709\u200b\u4e49\u52a1\u200b\u5411\u200b\u63a5\u53d7\u200b\u60a8\u200b\u4f20\u9001\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u4eba\u200b\u6536\u53d6\u200b\u7248\u7a0e\u200b\uff0c\u200b\u90a3\u4e48\u200b\u60a8\u200b\u8981\u200b\u540c\u65f6\u200b\u6ee1\u8db3\u200b\u8fd9\u4e9b\u200b\u6761\u6b3e\u200b\u548c\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u8981\u6c42\u200b\uff0c\u200b\u552f\u4e00\u200b\u7684\u200b\u529e\u6cd5\u200b\u5c31\u662f\u200b\u5b8c\u5168\u200b\u4e0d\u200b\u4f20\u9001\u200b\u8be5\u200b\u7a0b\u5e8f\u200b\u3002

"},{"location":"zh/about/license/#13-gnu","title":"13. \u200b\u8fdc\u7a0b\u200b\u7f51\u7edc\u200b\u4ea4\u4e92\u200b\uff1b\u200b\u4e0e\u200bGNU\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u4e00\u8d77\u200b\u4f7f\u7528\u200b.","text":"

\u200b\u5c3d\u7ba1\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6709\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u5982\u679c\u200b\u60a8\u200b\u4fee\u6539\u200b\u672c\u200b\u7a0b\u5e8f\u200b\uff0c\u200b\u60a8\u200b\u7684\u200b\u4fee\u6539\u200b\u7248\u672c\u200b\u5fc5\u987b\u200b\u5728\u200b\u663e\u8457\u200b\u4f4d\u7f6e\u200b\u5411\u200b\u6240\u6709\u200b\u901a\u8fc7\u200b\u8ba1\u7b97\u673a\u7f51\u7edc\u200b\u8fdc\u7a0b\u200b\u4e0e\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u4e92\u52a8\u200b\u7684\u200b\u7528\u6237\u200b\uff08\u200b\u5982\u679c\u200b\u60a8\u200b\u7684\u200b\u7248\u672c\u200b\u652f\u6301\u200b\u8fd9\u79cd\u200b\u4e92\u52a8\u200b\uff09\u200b\u63d0\u4f9b\u200b\u673a\u4f1a\u200b\uff0c\u200b\u901a\u8fc7\u200b\u4e00\u4e9b\u200b\u6807\u51c6\u200b\u6216\u200b\u4e60\u60ef\u200b\u7684\u200b\u4fc3\u8fdb\u200b\u8f6f\u4ef6\u200b\u590d\u5236\u200b\u7684\u200b\u65b9\u5f0f\u200b\uff0c\u200b\u4ece\u200b\u7f51\u7edc\u200b\u670d\u52a1\u5668\u200b\u4e0a\u200b\u514d\u8d39\u200b\u63d0\u4f9b\u200b\u76f8\u5e94\u200b\u7684\u200b\u6e90\u7801\u200b\u3002\u200b\u8be5\u200b\u76f8\u5e94\u200b\u6e90\u7801\u200b\u5e94\u200b\u5305\u62ec\u200b\u6839\u636e\u200b\u4e0b\u200b\u6bb5\u200b\u89c4\u5b9a\u200b\u7eb3\u5165\u200bGNU\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7b2c\u200b3\u200b\u7248\u200b\u7684\u200b\u4efb\u4f55\u200b\u4f5c\u54c1\u200b\u7684\u200b\u76f8\u5e94\u200b\u6e90\u7801\u200b\u3002

\u200b\u5c3d\u7ba1\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u6709\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u89c4\u5b9a\u200b\uff0c\u200b\u60a8\u200b\u6709\u200b\u6743\u5229\u200b\u5c06\u200b\u4efb\u4f55\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u7684\u200b\u4f5c\u54c1\u200b\u4e0e\u200b\u5728\u200bGNU\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7b2c\u200b3\u200b\u7248\u4e0b\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u4f5c\u54c1\u200b\u94fe\u63a5\u200b\u6216\u200b\u7ed3\u5408\u200b\u6210\u200b\u4e00\u4e2a\u200b\u5355\u4e00\u200b\u7684\u200b\u7ec4\u5408\u200b\u4f5c\u54c1\u200b\uff0c\u200b\u5e76\u200b\u4f20\u9012\u200b\u7531\u6b64\u200b\u4ea7\u751f\u200b\u7684\u200b\u4f5c\u54c1\u200b\u3002\u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u6761\u6b3e\u200b\u5c06\u200b\u7ee7\u7eed\u200b\u9002\u7528\u200b\u4e8e\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u4f5c\u54c1\u200b\u7684\u200b\u90e8\u5206\u200b\uff0c\u200b\u4f46\u200b\u4e0e\u200b\u4e4b\u200b\u7ed3\u5408\u200b\u7684\u200b\u4f5c\u54c1\u200b\u5c06\u200b\u7ee7\u7eed\u200b\u53d7\u200bGNU\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7b2c\u200b3\u200b\u7248\u200b\u7684\u200b\u7ba1\u8f96\u200b\u3002

"},{"location":"zh/about/license/#14","title":"14. \u200b\u672c\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u4fee\u8ba2\u7248\u200b.","text":"

\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u57fa\u91d1\u4f1a\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u4e0d\u65f6\u200b\u5730\u200b\u53d1\u5e03\u200bGNU Affero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u4fee\u8ba2\u7248\u200b\u548c\u200b/\u200b\u6216\u200b\u65b0\u200b\u7248\u672c\u200b\u3002\u200b\u8fd9\u4e9b\u200b\u65b0\u200b\u7248\u672c\u200b\u5728\u7cbe\u795e\u4e0a\u200b\u4e0e\u200b\u76ee\u524d\u200b\u7684\u200b\u7248\u672c\u200b\u76f8\u4f3c\u200b\uff0c\u200b\u4f46\u200b\u5728\u200b\u7ec6\u8282\u200b\u4e0a\u200b\u53ef\u80fd\u200b\u6709\u6240\u4e0d\u540c\u200b\uff0c\u200b\u4ee5\u200b\u89e3\u51b3\u200b\u65b0\u200b\u7684\u200b\u95ee\u9898\u200b\u6216\u200b\u5173\u5207\u200b\u3002

\u200b\u6bcf\u4e2a\u200b\u7248\u672c\u200b\u90fd\u200b\u6709\u200b\u4e00\u4e2a\u200b\u533a\u5206\u200b\u7684\u200b\u7248\u672c\u53f7\u200b\u3002\u200b\u5982\u679c\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u6307\u5b9a\u200b\u67d0\u4e2a\u200b\u7f16\u53f7\u200b\u7684\u200b GNU Affero \u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b \u201c\u200b\u6216\u200b\u4efb\u4f55\u200b\u540e\u6765\u200b\u7684\u200b\u7248\u672c\u200b\u201d \u200b\u9002\u7528\u200b\u4e8e\u200b\u5b83\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u9009\u62e9\u200b\u9075\u5b88\u200b\u8be5\u200b\u7f16\u53f7\u200b\u7684\u200b\u7248\u672c\u200b\u6216\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u57fa\u91d1\u4f1a\u200b\u53d1\u5e03\u200b\u7684\u200b\u4efb\u4f55\u200b\u540e\u6765\u200b\u7684\u200b\u7248\u672c\u200b\u7684\u200b\u6761\u6b3e\u200b\u548c\u200b\u6761\u4ef6\u200b\u3002\u200b\u5982\u679c\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u6ca1\u6709\u200b\u6307\u5b9a\u200b GNU Affero \u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u7248\u672c\u53f7\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u9009\u62e9\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u57fa\u91d1\u4f1a\u200b\u53d1\u5e03\u200b\u7684\u200b\u4efb\u4f55\u200b\u7248\u672c\u200b\u3002 \u200b\u5982\u679c\u200b\u672c\u200b\u8ba1\u5212\u200b\u89c4\u5b9a\u200b\u4ee3\u7406\u4eba\u200b\u53ef\u4ee5\u200b\u51b3\u5b9a\u200b\u672a\u6765\u200b\u53ef\u4ee5\u200b\u4f7f\u7528\u200b\u54ea\u4e2a\u200b\u7248\u672c\u200b\u7684\u200b GNU Affero \u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\uff0c\u200b\u90a3\u4e48\u200b\u8be5\u200b\u4ee3\u7406\u4eba\u200b\u5bf9\u200b\u67d0\u4e2a\u200b\u7248\u672c\u200b\u7684\u200b\u516c\u5f00\u200b\u63a5\u53d7\u200b\u58f0\u660e\u200b\u5c06\u200b\u6c38\u4e45\u200b\u6388\u6743\u200b\u60a8\u200b\u4e3a\u200b\u672c\u200b\u8ba1\u5212\u200b\u9009\u62e9\u200b\u8be5\u200b\u7248\u672c\u200b\u3002

\u200b\u4ee5\u540e\u200b\u7684\u200b\u8bb8\u53ef\u8bc1\u200b\u7248\u672c\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u7ed9\u200b\u60a8\u200b\u989d\u5916\u200b\u7684\u200b\u6216\u200b\u4e0d\u540c\u200b\u7684\u200b\u6743\u9650\u200b\u3002\u200b\u4f46\u662f\u200b\uff0c\u200b\u4efb\u4f55\u200b\u4f5c\u8005\u200b\u6216\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u90fd\u200b\u4e0d\u4f1a\u200b\u56e0\u4e3a\u200b\u60a8\u200b\u9009\u62e9\u200b\u4e86\u200b\u540e\u6765\u200b\u7684\u200b\u7248\u672c\u200b\u800c\u200b\u627f\u62c5\u200b\u989d\u5916\u200b\u7684\u200b\u4e49\u52a1\u200b\u3002

"},{"location":"zh/about/license/#15","title":"15. \u200b\u514d\u8d23\u200b\u58f0\u660e\u200b.","text":"

\u200b\u5728\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u5141\u8bb8\u200b\u7684\u200b\u8303\u56f4\u200b\u5185\u200b\uff0c\u200b\u5bf9\u200b\u8be5\u200b\u7a0b\u5e8f\u200b\u6ca1\u6709\u200b\u4efb\u4f55\u200b\u4fdd\u8bc1\u200b\u3002\u200b\u9664\u975e\u200b\u53e6\u6709\u200b\u4e66\u9762\u200b\u8bf4\u660e\u200b\uff0c\u200b\u5426\u5219\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u548c\u200b/\u200b\u6216\u200b\u5176\u4ed6\u200b\u5404\u65b9\u200b \u201c\u200b\u6309\u200b\u539f\u6837\u200b\u201d \u200b\u63d0\u4f9b\u200b\u8be5\u200b\u7a0b\u5e8f\u200b\uff0c\u200b\u4e0d\u200b\u63d0\u4f9b\u200b\u4efb\u4f55\u200b\u660e\u793a\u200b\u6216\u200b\u6697\u793a\u200b\u7684\u200b\u4fdd\u8bc1\u200b\uff0c\u200b\u5305\u62ec\u200b\u4f46\u200b\u4e0d\u200b\u9650\u4e8e\u200b\u5bf9\u200b\u9002\u9500\u200b\u6027\u200b\u548c\u200b\u7279\u5b9a\u200b\u7528\u9014\u200b\u7684\u200b\u9002\u7528\u6027\u200b\u7684\u200b\u6697\u793a\u200b\u4fdd\u8bc1\u200b\u3002\u200b\u5173\u4e8e\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u8d28\u91cf\u200b\u548c\u200b\u6027\u80fd\u200b\u7684\u200b\u5168\u90e8\u200b\u98ce\u9669\u200b\u7531\u200b\u60a8\u200b\u627f\u62c5\u200b\u3002\u200b\u5982\u679c\u200b\u8be5\u200b\u7a0b\u5e8f\u200b\u88ab\u200b\u8bc1\u660e\u200b\u6709\u200b\u7f3a\u9677\u200b\uff0c\u200b\u60a8\u200b\u5c06\u200b\u627f\u62c5\u200b\u6240\u6709\u200b\u5fc5\u8981\u200b\u7684\u200b\u670d\u52a1\u200b\u3001\u200b\u4fee\u7406\u200b\u6216\u200b\u7ea0\u6b63\u200b\u7684\u200b\u8d39\u7528\u200b\u3002

"},{"location":"zh/about/license/#16","title":"16. \u200b\u8d54\u507f\u200b\u8d23\u4efb\u200b\u7684\u200b\u9650\u5236\u200b.","text":"

\u200b\u5728\u200b\u4efb\u4f55\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u9664\u975e\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u8981\u6c42\u200b\u6216\u200b\u4e66\u9762\u200b\u540c\u610f\u200b\uff0c\u200b\u4efb\u4f55\u200b\u7248\u6743\u200b\u6301\u6709\u4eba\u200b\u6216\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u6309\u200b\u4e0a\u8ff0\u200b\u89c4\u5b9a\u200b\u4fee\u6539\u200b\u548c\u200b/\u200b\u6216\u200b\u4f20\u9012\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u5f53\u4e8b\u4eba\u200b\u90fd\u200b\u4e0d\u200b\u5bf9\u200b\u60a8\u200b\u7684\u200b\u635f\u5bb3\u200b\u8d1f\u8d23\u200b\uff0c\u200b\u5305\u62ec\u200b\u56e0\u200b\u4f7f\u7528\u200b\u6216\u200b\u65e0\u6cd5\u200b\u4f7f\u7528\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u800c\u200b\u5f15\u8d77\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e00\u822c\u200b\u7684\u200b\u3001\u200b\u7279\u6b8a\u200b\u7684\u200b\u3001\u200b\u5076\u7136\u200b\u7684\u200b\u6216\u200b\u95f4\u63a5\u200b\u7684\u200b\u635f\u5bb3\u200b\uff08\u200b\u5305\u62ec\u200b\u4f46\u200b\u4e0d\u200b\u9650\u4e8e\u200b\u6570\u636e\u200b\u4e22\u5931\u200b\u6216\u200b\u6570\u636e\u200b\u4e0d\u200b\u51c6\u786e\u200b\u6216\u200b\u60a8\u200b\u6216\u200b\u7b2c\u4e09\u65b9\u200b\u906d\u53d7\u200b\u7684\u200b\u635f\u5931\u200b\u6216\u672c\u200b\u7a0b\u5e8f\u200b\u65e0\u6cd5\u200b\u4e0e\u200b\u4efb\u4f55\u200b\u5176\u4ed6\u200b\u7a0b\u5e8f\u200b\u4e00\u8d77\u200b\u8fd0\u884c\u200b\uff09\uff0c\u200b\u5373\u4f7f\u200b\u8be5\u200b\u6301\u6709\u4eba\u200b\u6216\u200b\u5176\u4ed6\u200b\u5f53\u4e8b\u4eba\u200b\u5df2\u200b\u88ab\u200b\u544a\u77e5\u200b\u8fd9\u79cd\u200b\u635f\u5bb3\u200b\u7684\u200b\u53ef\u80fd\u6027\u200b\u3002

"},{"location":"zh/about/license/#17-1516","title":"17. \u200b\u7b2c\u200b15\u200b\u6761\u200b\u548c\u200b\u7b2c\u200b16\u200b\u6761\u200b\u7684\u200b\u89e3\u91ca\u200b.","text":"

\u200b\u5982\u679c\u200b\u4ee5\u4e0a\u200b\u89c4\u5b9a\u200b\u7684\u200b\u514d\u8d23\u200b\u58f0\u660e\u200b\u548c\u200b\u8d23\u4efb\u200b\u9650\u5236\u200b\u4e0d\u80fd\u200b\u6839\u636e\u200b\u5176\u200b\u6761\u6b3e\u200b\u5728\u200b\u5f53\u5730\u200b\u4ea7\u751f\u200b\u6cd5\u5f8b\u6548\u529b\u200b\uff0c\u200b\u5ba1\u67e5\u200b\u6cd5\u9662\u200b\u5e94\u200b\u9002\u7528\u200b\u6700\u200b\u63a5\u8fd1\u200b\u4e8e\u200b\u7edd\u5bf9\u200b\u653e\u5f03\u200b\u4e0e\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u6709\u5173\u200b\u7684\u200b\u6240\u6709\u200b\u6c11\u4e8b\u8d23\u4efb\u200b\u7684\u200b\u5f53\u5730\u200b\u6cd5\u5f8b\u200b\uff0c\u200b\u9664\u975e\u200b\u5728\u200b\u6536\u53d6\u200b\u8d39\u7528\u200b\u7684\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u526f\u672c\u200b\u4e0a\u200b\u9644\u6709\u200b\u4fdd\u8bc1\u200b\u6216\u200b\u8d23\u4efb\u200b\u627f\u62c5\u200b\u3002

\u200b\u4ee5\u4e0a\u200b\u662f\u200b\u6761\u6b3e\u200b\u548c\u200b\u6761\u4ef6\u200b

"},{"location":"zh/about/license/#_3","title":"\u5982\u4f55\u200b\u5c06\u200b\u8fd9\u4e9b\u200b\u6761\u6b3e\u200b\u5e94\u7528\u200b\u4e8e\u200b\u60a8\u200b\u7684\u200b\u65b0\u200b\u7a0b\u5e8f","text":"

\u200b\u5982\u679c\u200b\u60a8\u200b\u5f00\u53d1\u200b\u4e86\u200b\u4e00\u4e2a\u200b\u65b0\u200b\u7684\u200b\u7a0b\u5e8f\u200b\uff0c\u200b\u5e76\u200b\u5e0c\u671b\u200b\u5b83\u200b\u5bf9\u200b\u516c\u4f17\u200b\u6709\u200b\u6700\u5927\u200b\u7684\u200b\u7528\u5904\u200b\uff0c\u200b\u5b9e\u73b0\u200b\u8fd9\u4e00\u200b\u76ee\u6807\u200b\u7684\u200b\u6700\u597d\u200b\u65b9\u6cd5\u200b\u662f\u200b\u4f7f\u200b\u5b83\u200b\u6210\u4e3a\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\uff0c\u200b\u6bcf\u4e2a\u200b\u4eba\u200b\u90fd\u200b\u53ef\u4ee5\u200b\u5728\u200b\u8fd9\u4e9b\u200b\u6761\u6b3e\u200b\u4e0b\u200b\u91cd\u65b0\u200b\u53d1\u5e03\u200b\u548c\u200b\u4fee\u6539\u200b\u3002

\u200b\u8981\u200b\u505a\u5230\u200b\u8fd9\u200b\u4e00\u70b9\u200b\uff0c\u200b\u8bf7\u200b\u5728\u200b\u7a0b\u5e8f\u200b\u4e2d\u200b\u9644\u4e0a\u200b\u4ee5\u4e0b\u200b\u901a\u77e5\u200b\u3002\u200b\u6700\u200b\u5b89\u5168\u200b\u7684\u200b\u505a\u6cd5\u200b\u662f\u200b\u628a\u200b\u5b83\u4eec\u200b\u9644\u5728\u200b\u6bcf\u4e2a\u200b\u6e90\u6587\u4ef6\u200b\u7684\u200b\u5f00\u5934\u200b\uff0c\u200b\u4ee5\u200b\u6700\u200b\u6709\u6548\u200b\u5730\u200b\u8bf4\u660e\u200b\u6392\u9664\u200b\u62c5\u4fdd\u200b\u7684\u200b\u60c5\u51b5\u200b\uff1b\u200b\u6bcf\u4e2a\u200b\u6587\u4ef6\u200b\u81f3\u5c11\u200b\u8981\u200b\u6709\u200b \u201c\u200b\u7248\u6743\u200b\u201d \u200b\u4e00\u884c\u200b\u548c\u200b\u4e00\u4e2a\u200b\u6307\u5411\u200b\u5b8c\u6574\u200b\u901a\u77e5\u200b\u7684\u200b\u6307\u9488\u200b\u3002

Text Only
<one line to give the program's name and a brief idea of what it does.>\nCopyright (C) <year>  <name of author>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU Affero General Public License as\npublished by the Free Software Foundation, either version 3 of the\nLicense, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License\nalong with this program.  If not, see <https://www.gnu.org/licenses/>.\n

\u200b\u7ffb\u8bd1\u200b\uff1a

Text Only
<\u200b\u7528\u200b\u4e00\u884c\u200b\u5b57\u6765\u200b\u8bf4\u660e\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u540d\u79f0\u200b\u548c\u200b\u5b83\u200b\u6240\u200b\u505a\u200b\u7684\u200b\u4e8b\u60c5\u200b\u7684\u200b\u7b80\u5355\u200b\u6982\u5ff5\u200b\u3002>\nCopyright (C) <\u200b\u5e74\u200b> <\u200b\u4f5c\u8005\u59d3\u540d\u200b> \u200b\u7248\u6743\u6240\u6709\u200b\u3002\n\n\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u662f\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\uff1a\u200b\u4f60\u200b\u53ef\u4ee5\u200b\u6839\u636e\u200b\u81ea\u7531\u8f6f\u4ef6\u200b\u57fa\u91d1\u4f1a\u200b\u53d1\u5e03\u200b\u7684\u200bGNU Affero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u6761\u6b3e\u200b\uff0c\u200b\u5373\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u7b2c\u200b3\u200b\u7248\u200b\u6216\u200b\uff08\u200b\u60a8\u200b\u9009\u62e9\u200b\u7684\u200b\uff09\u200b\u4efb\u4f55\u200b\u540e\u6765\u200b\u7684\u200b\u7248\u672c\u200b\u91cd\u65b0\u200b\u53d1\u5e03\u200b\u5b83\u200b\u548c\u200b/\u200b\u6216\u200b\u4fee\u6539\u200b\u5b83\u200b\u3002\u3002\n\n\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u53d1\u5e03\u200b\u662f\u200b\u5e0c\u671b\u200b\u5b83\u200b\u80fd\u200b\u8d77\u5230\u200b\u4f5c\u7528\u200b\u3002\u200b\u4f46\u200b\u6ca1\u6709\u200b\u4efb\u4f55\u200b\u4fdd\u8bc1\u200b\uff1b\u200b\u751a\u81f3\u200b\u6ca1\u6709\u200b\u9690\u542b\u200b\u7684\u200b\u4fdd\u8bc1\u200b\u3002\u200b\u672c\u200b\u7a0b\u5e8f\u200b\u7684\u200b\u5206\u53d1\u200b\u662f\u200b\u5e0c\u671b\u200b\u5b83\u200b\u662f\u200b\u6709\u7528\u200b\u7684\u200b\uff0c\u200b\u4f46\u200b\u6ca1\u6709\u200b\u4efb\u4f55\u200b\u4fdd\u8bc1\u200b\uff0c\u200b\u751a\u81f3\u200b\u6ca1\u6709\u200b\u9690\u542b\u200b\u7684\u200b\u9002\u9500\u5bf9\u8def\u200b\u6216\u200b\u9002\u5408\u200b\u67d0\u4e00\u200b\u7279\u5b9a\u200b\u76ee\u7684\u200b\u7684\u200b\u4fdd\u8bc1\u200b\u3002 \u200b\u53c2\u89c1\u200b GNU Affero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u4e86\u89e3\u200b\u66f4\u200b\u591a\u200b\u7ec6\u8282\u200b\u3002\n\n\u200b\u60a8\u200b\u5e94\u8be5\u200b\u5df2\u7ecf\u200b\u6536\u5230\u200b\u4e86\u200b\u4e00\u4efd\u200bGNU Affero\u200b\u901a\u7528\u200b\u516c\u5171\u200b\u8bb8\u53ef\u8bc1\u200b\u7684\u200b\u526f\u672c\u200b\u3002 \u200b\u5982\u679c\u200b\u6ca1\u6709\u200b\uff0c\u200b\u8bf7\u200b\u53c2\u89c1\u200b<https://www.gnu.org/licenses/>\u3002\n\n\u200b\u8fd8\u8981\u200b\u589e\u52a0\u200b\u5982\u4f55\u200b\u901a\u8fc7\u200b\u7535\u5b50\u200b\u548c\u200b\u7eb8\u8d28\u200b\u90ae\u4ef6\u200b\u4e0e\u200b\u60a8\u200b\u8054\u7cfb\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002\n

\u200b\u5982\u679c\u200b\u60a8\u200b\u7684\u200b\u8f6f\u4ef6\u200b\u53ef\u4ee5\u200b\u901a\u8fc7\u200b\u8ba1\u7b97\u673a\u7f51\u7edc\u200b\u4e0e\u200b\u7528\u6237\u200b\u8fdb\u884c\u200b\u8fdc\u7a0b\u200b\u4ea4\u4e92\u200b\uff0c\u200b\u60a8\u200b\u4e5f\u200b\u5e94\u8be5\u200b\u786e\u4fdd\u200b\u5b83\u200b\u4e3a\u200b\u7528\u6237\u200b\u63d0\u4f9b\u200b\u4e00\u79cd\u200b\u83b7\u5f97\u200b\u5176\u200b\u6e90\u4ee3\u7801\u200b\u7684\u200b\u65b9\u6cd5\u200b\u3002\u200b\u4f8b\u5982\u200b\uff0c\u200b\u5982\u679c\u200b\u60a8\u200b\u7684\u200b\u7a0b\u5e8f\u200b\u662f\u200b\u4e00\u4e2a\u200b\u7f51\u7edc\u5e94\u7528\u200b\u7a0b\u5e8f\u200b\uff0c\u200b\u5b83\u200b\u7684\u200b\u754c\u9762\u200b\u53ef\u4ee5\u200b\u663e\u793a\u200b\u4e00\u4e2a\u200b \u201c\u200b\u6e90\u4ee3\u7801\u200b\u201d \u200b\u7684\u200b\u94fe\u63a5\u200b\uff0c\u200b\u5f15\u5bfc\u200b\u7528\u6237\u200b\u8fdb\u5165\u200b\u4ee3\u7801\u200b\u7684\u200b\u5b58\u6863\u200b\u3002\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u7528\u200b\u5f88\u591a\u200b\u65b9\u6cd5\u200b\u63d0\u4f9b\u200b\u6e90\u7801\u200b\uff0c\u200b\u4e0d\u540c\u200b\u7684\u200b\u89e3\u51b3\u65b9\u6848\u200b\u5bf9\u200b\u4e0d\u540c\u200b\u7684\u200b\u7a0b\u5e8f\u200b\u4f1a\u200b\u66f4\u597d\u200b\uff1b\u200b\u5177\u4f53\u200b\u8981\u6c42\u200b\u89c1\u200b\u7b2c\u200b13\u200b\u8282\u200b\u3002

\u200b\u5982\u679c\u200b\u6709\u200b\u5fc5\u8981\u200b\uff0c\u200b\u60a8\u200b\u8fd8\u200b\u5e94\u8be5\u200b\u8ba9\u200b\u60a8\u200b\u7684\u200b\u96c7\u4e3b\u200b\uff08\u200b\u5982\u679c\u200b\u60a8\u200b\u662f\u200b\u7a0b\u5e8f\u5458\u200b\uff09\u200b\u6216\u200b\u5b66\u6821\u200b\uff08\u200b\u5982\u679c\u200b\u6709\u200b\u7684\u8bdd\u200b\uff09\u200b\u4e3a\u200b\u8be5\u200b\u7a0b\u5e8f\u200b\u7b7e\u7f72\u200b\u4e00\u4efd\u200b \u201c\u200b\u7248\u6743\u200b\u514d\u8d23\u200b\u58f0\u660e\u200b\u201d\u3002\u200b\u6709\u5173\u200b\u8fd9\u65b9\u9762\u200b\u7684\u200b\u66f4\u200b\u591a\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u5982\u4f55\u200b\u7533\u8bf7\u200b\u548c\u200b\u9075\u5b88\u200bGNU AGPL\uff0c\u200b\u8bf7\u200b\u53c2\u89c1\u200bhttps://www.gnu.org/licenses/\u3002

"},{"location":"zh/about/privacy/","title":"Privacy Notice","text":"

\u200b\u7ffb\u8bd1\u200b

\u200b\u672c\u6587\u200b\u5185\u5bb9\u200b\u4e3a\u200b\u673a\u5668\u7ffb\u8bd1\u200b\u7248\u672c\u200b\uff0c\u200b\u65e8\u5728\u200b\u4e3a\u200b\u7528\u6237\u200b\u63d0\u4f9b\u65b9\u4fbf\u200b\u3002 \u200b\u6211\u4eec\u200b\u5df2\u7ecf\u200b\u5c3d\u529b\u200b\u786e\u4fdd\u200b\u7ffb\u8bd1\u200b\u7684\u200b\u51c6\u786e\u6027\u200b\u3002 \u200b\u4f46\u200b\u8bf7\u200b\u6ce8\u610f\u200b\uff0c\u200b\u7ffb\u8bd1\u200b\u5185\u5bb9\u200b\u53ef\u80fd\u200b\u5305\u542b\u200b\u9519\u8bef\u200b\uff0c\u200b\u4ec5\u4f9b\u53c2\u8003\u200b\u3002 \u200b\u8bf7\u4ee5\u200b\u82f1\u6587\u200b\u539f\u6587\u200b\u4e3a\u51c6\u200b\u3002

\u200b\u4e3a\u200b\u6ee1\u8db3\u200b\u5408\u89c4\u6027\u200b\u4e0e\u200b\u6267\u6cd5\u200b\u8981\u6c42\u200b\uff0c\u200b\u7ffb\u8bd1\u200b\u6587\u6863\u200b\u4e2d\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e0d\u200b\u51c6\u786e\u200b\u6216\u200b\u6b67\u4e49\u200b\u4e4b\u5904\u200b\u5747\u200b\u4e0d\u200b\u5177\u6709\u200b\u7ea6\u675f\u529b\u200b\uff0c\u200b\u4e5f\u200b\u4e0d\u200b\u5177\u5907\u200b\u6cd5\u5f8b\u6548\u529b\u200b\u3002

\u200b\u6700\u540e\u200b\u4fee\u8ba2\u200b\u65e5\u671f\u200b

\u200b\u672c\u200b\u58f0\u660e\u200b\u6700\u540e\u200b\u66f4\u65b0\u200b\u4e8e\u200b2024\u200b\u5e74\u200b5\u200b\u6708\u200b4\u200b\u65e5\u200b\u3002

"},{"location":"zh/about/privacy/#_1","title":"\u9690\u79c1\u200b\u58f0\u660e","text":"

\u200b\u672c\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u9002\u7528\u200b\u4e8e\u4e39\u7075\u200b\u56e2\u961f\u200b\uff08\u200b\u4e5f\u200b\u88ab\u79f0\u4f5c\u200b\u4e39\u7075\u200b\uff09\uff08\u200b\u4ee5\u4e0b\u200b\u7b80\u79f0\u200b\u201c\u200b\u6211\u4eec\u200b\u201d\uff09\uff0c\u200b\u63cf\u8ff0\u200b\u4e86\u200b\u5f53\u200b\u60a8\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\uff08\u201c\u200b\u670d\u52a1\u200b\u201d\uff09\u200b\u65f6\u200b\uff0c\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u4ee5\u53ca\u200b\u4e3a\u4f55\u200b\u53ef\u80fd\u200b\u6536\u96c6\u200b\u3001\u200b\u5b58\u50a8\u200b\u3001\u200b\u4f7f\u7528\u200b\u548c\u200b/\u200b\u6216\u200b\u5171\u4eab\u200b\uff08\u201c\u200b\u5904\u7406\u200b\u201d\uff09\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002\u200b\u4f8b\u5982\u200b\u5f53\u200b\u60a8\u200b\uff1a

  • \u200b\u8bbf\u95ee\u200b\u6211\u4eec\u200b\u7684\u200b\u7f51\u7ad9\u200b chanfig.danling.org \u200b\u6216\u200b\u4efb\u4f55\u200b\u94fe\u63a5\u200b\u5230\u200b\u672c\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u7684\u200b\u6211\u4eec\u200b\u7684\u200b\u7f51\u7ad9\u200b\u65f6\u200b

\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u968f\u65f6\u200b\u901a\u8fc7\u200b\u70b9\u51fb\u200b\u4e0b\u9762\u200b\u7684\u200b\u6309\u94ae\u200b\u66f4\u6539\u200b\u60a8\u200b\u7684\u200b\u9690\u79c1\u200b\u8bbe\u7f6e\u200b\uff1a

\u200b\u9690\u79c1\u200b\u63a7\u5236\u200b

\u200b\u6709\u200b\u95ee\u9898\u200b\u6216\u200b\u5173\u6ce8\u200b\uff1f \u200b\u9605\u8bfb\u200b\u672c\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u5c06\u200b\u5e2e\u52a9\u200b\u60a8\u200b\u4e86\u89e3\u200b\u60a8\u200b\u7684\u200b\u9690\u79c1\u200b\u6743\u5229\u200b\u548c\u200b\u9009\u62e9\u200b\u3002 \u200b\u5982\u679c\u200b\u60a8\u200b\u4e0d\u200b\u540c\u610f\u200b\u6211\u4eec\u200b\u7684\u200b\u58f0\u660e\u200b\u548c\u200b\u505a\u6cd5\u200b\uff0c\u200b\u8bf7\u200b\u4e0d\u8981\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u3002 \u200b\u5982\u679c\u200b\u60a8\u200b\u4ecd\u200b\u6709\u200b\u4efb\u4f55\u200b\u95ee\u9898\u200b\u6216\u200b\u5173\u6ce8\u200b\uff0c\u200b\u8bf7\u200b\u901a\u8fc7\u200bprivacy@danling.org\u200b\u4e0e\u200b\u6211\u4eec\u200b\u8054\u7cfb\u200b\u3002

"},{"location":"zh/about/privacy/#0","title":"0. \u200b\u5173\u952e\u70b9\u200b\u603b\u7ed3","text":"

\u200b\u672c\u200b\u603b\u7ed3\u200b\u63d0\u4f9b\u200b\u4e86\u200b\u6211\u4eec\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u7684\u200b\u5173\u952e\u70b9\u200b\uff0c\u200b\u4f46\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u901a\u8fc7\u200b\u70b9\u51fb\u200b\u6bcf\u4e2a\u200b\u5173\u952e\u70b9\u200b\u540e\u200b\u7684\u200b\u94fe\u63a5\u200b\u6216\u200b\u4f7f\u7528\u200b\u76ee\u5f55\u200b\u6765\u200b\u627e\u5230\u200b\u60a8\u200b\u6240\u200b\u67e5\u627e\u200b\u7684\u200b\u90e8\u5206\u200b\u4ee5\u200b\u4e86\u89e3\u200b\u66f4\u200b\u591a\u200b\u8be6\u60c5\u200b\u3002

\u200b\u6211\u4eec\u200b\u5904\u7406\u200b\u54ea\u4e9b\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1f

\u200b\u5f53\u200b\u60a8\u200b\u8bbf\u95ee\u200b\u3001\u200b\u4f7f\u7528\u200b\u6216\u200b\u5bfc\u822a\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u65f6\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u6839\u636e\u200b\u60a8\u200b\u4e0e\u200b\u6211\u4eec\u200b\u4ee5\u53ca\u200b\u670d\u52a1\u200b\u7684\u200b\u4e92\u52a8\u200b\u65b9\u5f0f\u200b\u3001\u200b\u60a8\u200b\u6240\u200b\u505a\u200b\u7684\u200b\u9009\u62e9\u200b\u4ee5\u53ca\u200b\u60a8\u200b\u4f7f\u7528\u200b\u7684\u200b\u4ea7\u54c1\u200b\u548c\u200b\u529f\u80fd\u200b\u6765\u200b\u5904\u7406\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u54ea\u4e9b\u200b\u4fe1\u606f\u200b\uff1f

\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff1f

\u200b\u6211\u4eec\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u4ee5\u200b\u63d0\u4f9b\u200b\u3001\u200b\u6539\u5584\u200b\u548c\u200b\u7ba1\u7406\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\uff0c\u200b\u4e0e\u200b\u60a8\u200b\u6c9f\u901a\u200b\uff0c\u200b\u8fdb\u884c\u200b\u5b89\u5168\u200b\u548c\u200b\u9632\u200b\u6b3a\u8bc8\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u9075\u5b88\u200b\u6cd5\u5f8b\u200b\u3002 \u200b\u6211\u4eec\u200b\u4e5f\u200b\u53ef\u80fd\u200b\u5728\u200b\u5f97\u5230\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u51fa\u4e8e\u200b\u5176\u4ed6\u200b\u76ee\u7684\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002 \u200b\u6211\u4eec\u200b\u4ec5\u200b\u5728\u200b\u6709\u200b\u5408\u6cd5\u200b\u6cd5\u5f8b\u200b\u7406\u7531\u200b\u65f6\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff1f

\u200b\u6211\u4eec\u200b\u5904\u7406\u200b\u4efb\u4f55\u200b\u654f\u611f\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u5417\u200b\uff1f

\u200b\u6211\u4eec\u200b\u4e0d\u200b\u5904\u7406\u200b\u4efb\u4f55\u200b\u654f\u611f\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u4ece\u200b\u7b2c\u4e09\u65b9\u200b\u6536\u96c6\u200b\u4fe1\u606f\u200b\u5417\u200b\uff1f

\u200b\u6211\u4eec\u200b\u4e0d\u200b\u4ece\u200b\u7b2c\u4e09\u65b9\u200b\u6536\u96c6\u200b\u4efb\u4f55\u200b\u4fe1\u606f\u200b\u3002

\u200b\u5728\u200b\u54ea\u4e9b\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u4ee5\u53ca\u200b\u4e0e\u200b\u54ea\u4e9b\u200b\u65b9\u200b\u6211\u4eec\u200b\u5171\u4eab\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1f

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u5728\u200b\u7279\u5b9a\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u4e0e\u200b\u7279\u5b9a\u200b\u7b2c\u4e09\u65b9\u200b\u5171\u4eab\u200b\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u4f55\u65f6\u200b\u4ee5\u53ca\u200b\u4e0e\u200b\u8c01\u200b\u5171\u4eab\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1f

\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u5b89\u5168\u200b\uff1f

\u200b\u6211\u4eec\u200b\u5df2\u7ecf\u200b\u5b9e\u65bd\u200b\u4e86\u200b\u7ec4\u7ec7\u200b\u548c\u200b\u6280\u672f\u200b\u6d41\u7a0b\u200b\u548c\u200b\u7a0b\u5e8f\u200b\u6765\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u4fdd\u6301\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u5b89\u5168\u200b\uff1f

\u200b\u60a8\u200b\u6709\u200b\u54ea\u4e9b\u200b\u6743\u5229\u200b\uff1f

\u200b\u6839\u636e\u200b\u60a8\u200b\u6240\u5728\u200b\u5730\u7406\u4f4d\u7f6e\u200b\uff0c\u200b\u9002\u7528\u200b\u7684\u200b\u9690\u79c1\u200b\u6cd5\u200b\u53ef\u80fd\u200b\u610f\u5473\u7740\u200b\u60a8\u200b\u5bf9\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u6709\u200b\u67d0\u4e9b\u200b\u6743\u5229\u200b\u3002

\u200b\u60a8\u200b\u6709\u200b\u54ea\u4e9b\u200b\u9690\u79c1\u200b\u6743\u5229\u200b\uff1f

\u200b\u60a8\u200b\u5982\u4f55\u200b\u884c\u4f7f\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b\uff1f

\u200b\u884c\u4f7f\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b\u7684\u200b\u6700\u200b\u7b80\u5355\u200b\u65b9\u5f0f\u200b\u662f\u200b\u8054\u7cfb\u200b\u60a8\u200b\u7684\u200b\u53f8\u6cd5\u200b\u7ba1\u8f96\u533a\u200b\u7684\u200b\u76f8\u5173\u200b\u6570\u636e\u4fdd\u62a4\u200b\u76d1\u7ba1\u200b\u673a\u6784\u200b\u3002

\u200b\u5982\u4f55\u200b\u884c\u4f7f\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b\uff1f

"},{"location":"zh/about/privacy/#1","title":"1. \u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u54ea\u4e9b\u200b\u4fe1\u606f\u200b\uff1f","text":""},{"location":"zh/about/privacy/#_2","title":"\u60a8\u200b\u5411\u200b\u6211\u4eec\u200b\u62ab\u9732\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u60a8\u200b\u5411\u200b\u6211\u4eec\u200b\u63d0\u4f9b\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u60a8\u200b\u81ea\u613f\u200b\u5411\u200b\u6211\u4eec\u200b\u63d0\u4f9b\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u5f53\u200b\u60a8\u200b\u8868\u8fbe\u200b\u5bf9\u200b\u6211\u4eec\u200b\u6216\u200b\u6211\u4eec\u200b\u7684\u200b\u4ea7\u54c1\u200b\u548c\u200b\u670d\u52a1\u200b\u7684\u200b\u5174\u8da3\u200b\u3001\u200b\u53c2\u4e0e\u200b\u670d\u52a1\u200b\u4e0a\u200b\u7684\u200b\u6d3b\u52a8\u200b\u6216\u200b\u4ee5\u200b\u5176\u4ed6\u200b\u65b9\u5f0f\u200b\u8054\u7cfb\u200b\u6211\u4eec\u200b\u65f6\u200b\u3002

\u200b\u654f\u611f\u200b\u4e2a\u4eba\u4fe1\u606f\u200b

\u200b\u6211\u4eec\u200b\u4e0d\u200b\u4ece\u200b\u60a8\u200b\u90a3\u91cc\u200b\u6536\u96c6\u200b\u4efb\u4f55\u200b\u654f\u611f\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

"},{"location":"zh/about/privacy/#_3","title":"\u81ea\u52a8\u200b\u6536\u96c6\u200b\u7684\u200b\u4fe1\u606f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u5f53\u200b\u60a8\u200b\u8bbf\u95ee\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u65f6\u200b\uff0c\u200b\u67d0\u4e9b\u200b\u4fe1\u606f\u200b\u2014\u2014\u200b\u5982\u200bIP\u200b\u5730\u5740\u200b\u548c\u200b/\u200b\u6216\u200b\u6d4f\u89c8\u5668\u200b\u548c\u200b\u8bbe\u5907\u200b\u7279\u5f81\u200b\u2014\u2014\u200b\u4f1a\u200b\u81ea\u52a8\u200b\u88ab\u200b\u6536\u96c6\u200b\u3002

\u200b\u6211\u4eec\u200b\u5728\u200b\u60a8\u200b\u8bbf\u95ee\u200b\u3001\u200b\u4f7f\u7528\u200b\u6216\u200b\u5bfc\u822a\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u65f6\u200b\u81ea\u52a8\u200b\u6536\u96c6\u200b\u67d0\u4e9b\u200b\u4fe1\u606f\u200b\u3002 \u200b\u8fd9\u4e9b\u200b\u4fe1\u606f\u200b\u4e0d\u4f1a\u200b\u900f\u9732\u200b\u60a8\u200b\u7684\u200b\u7279\u5b9a\u200b\u8eab\u4efd\u200b\uff08\u200b\u5982\u200b\u60a8\u200b\u7684\u200b\u59d3\u540d\u200b\u6216\u200b\u8054\u7cfb\u200b\u4fe1\u606f\u200b\uff09\uff0c\u200b\u4f46\u200b\u53ef\u80fd\u200b\u5305\u62ec\u200b\u8bbe\u5907\u200b\u548c\u200b\u4f7f\u7528\u200b\u4fe1\u606f\u200b\uff0c\u200b\u5982\u200b\u60a8\u200b\u7684\u200bIP\u200b\u5730\u5740\u200b\u3001\u200b\u6d4f\u89c8\u5668\u200b\u548c\u200b\u8bbe\u5907\u200b\u7279\u6027\u200b\u3001\u200b\u64cd\u4f5c\u7cfb\u7edf\u200b\u3001\u200b\u8bed\u8a00\u200b\u504f\u597d\u200b\u3001\u200b\u5f15\u7528\u200bURL\u3001\u200b\u8bbe\u5907\u200b\u540d\u79f0\u200b\u3001\u200b\u56fd\u5bb6\u200b\u3001\u200b\u4f4d\u7f6e\u200b\u3001\u200b\u6709\u5173\u200b\u60a8\u200b\u5982\u4f55\u200b\u4ee5\u53ca\u200b\u4f55\u65f6\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u5176\u4ed6\u200b\u6280\u672f\u200b\u4fe1\u606f\u200b\u3002 \u200b\u8fd9\u4e9b\u200b\u4fe1\u606f\u200b\u4e3b\u8981\u200b\u662f\u200b\u4e3a\u4e86\u200b\u7ef4\u62a4\u200b\u6211\u4eec\u200b\u670d\u52a1\u200b\u7684\u200b\u5b89\u5168\u6027\u200b\u548c\u200b\u8fd0\u4f5c\u200b\u6240\u200b\u9700\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u6211\u4eec\u200b\u5185\u90e8\u200b\u7684\u200b\u5206\u6790\u200b\u548c\u200b\u62a5\u544a\u200b\u76ee\u7684\u200b\u3002

\u200b\u50cf\u200b\u8bb8\u591a\u200b\u4f01\u4e1a\u200b\u4e00\u6837\u200b\uff0c\u200b\u6211\u4eec\u200b\u8fd8\u200b\u901a\u8fc7\u200bcookies\u200b\u548c\u200b\u7c7b\u4f3c\u200b\u6280\u672f\u200b\u6536\u96c6\u200b\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u7684\u200b\u4fe1\u606f\u200b\u5305\u62ec\u200b\uff1a

  • \u200b\u6807\u8bc6\u7b26\u200b\u3002 \u200b\u6807\u8bc6\u7b26\u200b\u662f\u200b\u5f53\u200b\u60a8\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u65f6\u200b\uff0c\u200b\u6211\u4eec\u200b\u751f\u6210\u200b\u7684\u200b\u7279\u5b9a\u200b\u4e8e\u200b\u8bbe\u5907\u200b\u548c\u200b\u6d4f\u89c8\u5668\u200b\u7684\u200b\u552f\u4e00\u200b\u968f\u673a\u200b\u5b57\u7b26\u4e32\u200b\u3002 \u200b\u8be5\u200b\u6807\u8bc6\u7b26\u200b\u5b58\u50a8\u200b\u5728\u200b\u60a8\u200b\u8bbe\u5907\u200b\u4e0a\u200b\u7684\u200b\u4e00\u4e2a\u200bcookie\u200b\u4e2d\u200b\uff0c\u200b\u4f7f\u200b\u6211\u4eec\u200b\u80fd\u591f\u200b\u5728\u200b\u591a\u4e2a\u200b\u4f1a\u8bdd\u200b\u4e2d\u200b\u4ee5\u53ca\u200b\u60a8\u200b\u8fd4\u56de\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u65f6\u200b\u8bc6\u522b\u200b\u60a8\u200b\u3002 \u200b\u60a8\u200b\u53ef\u4ee5\u200b\u901a\u8fc7\u200b\u6e05\u9664\u200b\u6d4f\u89c8\u5668\u200b\u7f13\u5b58\u200b\u968f\u65f6\u200b\u5220\u9664\u200b\u6b64\u200bcookie\u3002
  • \u200b\u65e5\u5fd7\u200b\u548c\u200b\u4f7f\u7528\u200b\u6570\u636e\u200b\u3002 \u200b\u65e5\u5fd7\u200b\u548c\u200b\u4f7f\u7528\u200b\u6570\u636e\u200b\u662f\u200b\u4e0e\u200b\u670d\u52a1\u200b\u76f8\u5173\u200b\u7684\u200b\u3001\u200b\u8bca\u65ad\u200b\u3001\u200b\u4f7f\u7528\u200b\u548c\u200b\u6027\u80fd\u200b\u4fe1\u606f\u200b\uff0c\u200b\u5f53\u200b\u60a8\u200b\u8bbf\u95ee\u200b\u6216\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u65f6\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u5668\u200b\u4f1a\u200b\u81ea\u52a8\u200b\u6536\u96c6\u200b\uff0c\u200b\u5e76\u200b\u8bb0\u5f55\u200b\u5728\u200b\u65e5\u5fd7\u200b\u6587\u4ef6\u200b\u4e2d\u200b\u3002 \u200b\u6839\u636e\u200b\u60a8\u200b\u4e0e\u200b\u6211\u4eec\u200b\u7684\u200b\u4e92\u52a8\u200b\u65b9\u5f0f\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u65e5\u5fd7\u200b\u6570\u636e\u200b\u53ef\u80fd\u200b\u5305\u62ec\u200b\u60a8\u200b\u7684\u200bIP\u200b\u5730\u5740\u200b\u3001\u200b\u8bbe\u5907\u200b\u4fe1\u606f\u200b\u3001\u200b\u6d4f\u89c8\u5668\u200b\u7c7b\u578b\u200b\u548c\u200b\u8bbe\u7f6e\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u6709\u5173\u200b\u60a8\u200b\u5728\u200b\u670d\u52a1\u200b\u4e2d\u200b\u7684\u200b\u6d3b\u52a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff08\u200b\u5982\u200b\u4e0e\u200b\u60a8\u200b\u4f7f\u7528\u200b\u76f8\u5173\u200b\u7684\u200b\u65e5\u671f\u200b/\u200b\u65f6\u95f4\u200b\u6233\u200b\u3001\u200b\u6d4f\u89c8\u200b\u548c\u200b\u67e5\u770b\u200b\u7684\u200b\u9875\u9762\u200b\u548c\u200b\u6587\u4ef6\u200b\u3001\u200b\u641c\u7d22\u200b\u4ee5\u53ca\u200b\u60a8\u200b\u91c7\u53d6\u200b\u7684\u200b\u5176\u4ed6\u200b\u64cd\u4f5c\u200b\uff0c\u200b\u4f8b\u5982\u200b\u60a8\u200b\u4f7f\u7528\u200b\u7684\u200b\u529f\u80fd\u200b\uff09\uff0c\u200b\u8bbe\u5907\u200b\u4e8b\u4ef6\u200b\u4fe1\u606f\u200b\uff08\u200b\u5982\u200b\u7cfb\u7edf\u6d3b\u52a8\u200b\u3001\u200b\u9519\u8bef\u62a5\u544a\u200b\uff08\u200b\u6709\u65f6\u200b\u79f0\u4e3a\u200b\u2019\u200b\u5d29\u6e83\u200b\u8f6c\u50a8\u200b\u2019\uff09\u200b\u548c\u200b\u786c\u4ef6\u200b\u8bbe\u7f6e\u200b\uff09\u3002
  • \u200b\u8bbe\u5907\u200b\u6570\u636e\u200b\u3002 \u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u8bbe\u5907\u200b\u6570\u636e\u200b\uff0c\u200b\u5982\u200b\u60a8\u200b\u7528\u4e8e\u200b\u8bbf\u95ee\u200b\u670d\u52a1\u200b\u7684\u200b\u8ba1\u7b97\u673a\u200b\u3001\u200b\u7535\u8bdd\u200b\u3001\u200b\u5e73\u677f\u200b\u6216\u200b\u5176\u4ed6\u200b\u8bbe\u5907\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002 \u200b\u6839\u636e\u200b\u6240\u200b\u4f7f\u7528\u200b\u7684\u200b\u8bbe\u5907\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u8bbe\u5907\u200b\u6570\u636e\u200b\u53ef\u80fd\u200b\u5305\u62ec\u200b\u5982\u4e0b\u200b\u4fe1\u606f\u200b\uff1a\u200b\u60a8\u200b\u7684\u200bIP\u200b\u5730\u5740\u200b\uff08\u200b\u6216\u200b\u4ee3\u7406\u670d\u52a1\u5668\u200b\uff09\u3001\u200b\u8bbe\u5907\u200b\u548c\u200b\u5e94\u7528\u7a0b\u5e8f\u200b\u8bc6\u522b\u200b\u53f7\u200b\u3001\u200b\u4f4d\u7f6e\u200b\u3001\u200b\u6d4f\u89c8\u5668\u200b\u7c7b\u578b\u200b\u3001\u200b\u786c\u4ef6\u200b\u578b\u53f7\u200b\u3001\u200b\u4e92\u8054\u7f51\u200b\u670d\u52a1\u63d0\u4f9b\u5546\u200b\u548c\u200b/\u200b\u6216\u200b\u79fb\u52a8\u200b\u8fd0\u8425\u5546\u200b\u3001\u200b\u64cd\u4f5c\u7cfb\u7edf\u200b\u548c\u200b\u7cfb\u7edf\u914d\u7f6e\u200b\u4fe1\u606f\u200b\u3002
  • \u200b\u4f4d\u7f6e\u200b\u6570\u636e\u200b\u3002 \u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u4f4d\u7f6e\u200b\u6570\u636e\u200b\uff0c\u200b\u5982\u200b\u60a8\u200b\u8bbe\u5907\u200b\u7684\u200b\u4f4d\u7f6e\u200b\u4fe1\u606f\u200b\uff0c\u200b\u8fd9\u200b\u53ef\u4ee5\u200b\u662f\u200b\u7cbe\u786e\u200b\u7684\u200b\u4e5f\u200b\u53ef\u4ee5\u200b\u662f\u200b\u4e0d\u200b\u7cbe\u786e\u200b\u7684\u200b\u3002 \u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u591a\u5c11\u200b\u4fe1\u606f\u200b\u53d6\u51b3\u4e8e\u200b\u60a8\u200b\u7528\u4e8e\u200b\u8bbf\u95ee\u200b\u670d\u52a1\u200b\u7684\u200b\u8bbe\u5907\u200b\u7684\u200b\u7c7b\u578b\u200b\u548c\u200b\u8bbe\u7f6e\u200b\u3002 \u200b\u4f8b\u5982\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f7f\u7528\u200bGPS\u200b\u548c\u200b\u5176\u4ed6\u200b\u6280\u672f\u200b\u6765\u200b\u6536\u96c6\u200b\u5730\u7406\u4f4d\u7f6e\u200b\u6570\u636e\u200b\uff0c\u200b\u544a\u8bc9\u200b\u6211\u4eec\u200b\u60a8\u200b\u5f53\u524d\u200b\u7684\u200b\u4f4d\u7f6e\u200b\uff08\u200b\u57fa\u4e8e\u200b\u60a8\u200b\u7684\u200bIP\u200b\u5730\u5740\u200b\uff09\u3002 \u200b\u60a8\u200b\u53ef\u4ee5\u200b\u9009\u62e9\u200b\u4e0d\u8ba9\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u6b64\u200b\u4fe1\u606f\u200b\uff0c\u200b\u65b9\u6cd5\u200b\u662f\u200b\u62d2\u7edd\u200b\u8bbf\u95ee\u4fe1\u606f\u200b\u6216\u200b\u5728\u200b\u60a8\u200b\u7684\u200b\u8bbe\u5907\u200b\u4e0a\u200b\u7981\u7528\u200b\u4f4d\u7f6e\u200b\u8bbe\u7f6e\u200b\u3002
"},{"location":"zh/about/privacy/#_4","title":"\u6211\u4eec\u200b\u6536\u96c6\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7c7b\u522b","text":"

\u200b\u8fc7\u53bb\u200b\u5341\u4e8c\u200b\uff0812\uff09\u200b\u4e2a\u200b\u6708\u200b\u5185\u200b\uff0c\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u4e86\u200b\u4ee5\u4e0b\u200b\u7c7b\u522b\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1a

\u200b\u7c7b\u522b\u200b \u200b\u793a\u4f8b\u200b \u200b\u5df2\u200b\u6536\u96c6\u200b A. \u200b\u6807\u8bc6\u7b26\u200b \u200b\u8054\u7cfb\u65b9\u5f0f\u200b\uff0c\u200b\u5982\u200b\u771f\u5b9e\u200b\u59d3\u540d\u200b\u3001\u200b\u522b\u540d\u200b\u3001\u200b\u90ae\u653f\u200b\u5730\u5740\u200b\u3001\u200b\u7535\u8bdd\u200b\u6216\u200b\u79fb\u52a8\u200b\u8054\u7cfb\u200b\u53f7\u7801\u200b\u3001\u200b\u72ec\u7279\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6807\u8bc6\u7b26\u200b\u3001\u200b\u5728\u7ebf\u200b\u6807\u8bc6\u7b26\u200b\u3001\u200b\u4e92\u8054\u7f51\u534f\u8bae\u200b\u5730\u5740\u200b\u3001\u200b\u7535\u5b50\u90ae\u4ef6\u200b\u5730\u5740\u200b\u548c\u200b\u5e10\u6237\u200b\u540d\u79f0\u200b \u200b\u662f\u200b B. \u200b\u52a0\u5229\u798f\u5c3c\u4e9a\u200b\u5ba2\u6237\u200b\u8bb0\u5f55\u200b\u6cd5\u4e2d\u200b\u5b9a\u4e49\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b \u200b\u59d3\u540d\u200b\u3001\u200b\u8054\u7cfb\u200b\u4fe1\u606f\u200b\u3001\u200b\u6559\u80b2\u200b\u3001\u200b\u5c31\u4e1a\u200b\u3001\u200b\u5c31\u4e1a\u200b\u5386\u53f2\u200b\u548c\u200b\u8d22\u52a1\u200b\u4fe1\u606f\u200b \u200b\u5426\u200b C. \u200b\u5dde\u200b\u6216\u200b\u8054\u90a6\u200b\u6cd5\u5f8b\u200b\u4e0b\u200b\u7684\u200b\u53d7\u200b\u4fdd\u62a4\u200b\u5206\u7c7b\u200b\u7279\u5f81\u200b \u200b\u6027\u522b\u200b\u3001\u200b\u5e74\u9f84\u200b\u3001\u200b\u51fa\u751f\u65e5\u671f\u200b\u3001\u200b\u79cd\u65cf\u200b\u548c\u200b\u6c11\u65cf\u200b\u3001\u200b\u56fd\u7c4d\u200b\u3001\u200b\u5a5a\u59fb\u72b6\u51b5\u200b\u548c\u200b\u5176\u4ed6\u200b\u4eba\u53e3\u200b\u7edf\u8ba1\u6570\u636e\u200b \u200b\u5426\u200b D. \u200b\u5546\u4e1a\u4fe1\u606f\u200b \u200b\u4ea4\u6613\u200b\u4fe1\u606f\u200b\u3001\u200b\u8d2d\u4e70\u200b\u5386\u53f2\u200b\u3001\u200b\u8d22\u52a1\u200b\u8be6\u7ec6\u4fe1\u606f\u200b\u548c\u200b\u652f\u4ed8\u200b\u4fe1\u606f\u200b \u200b\u5426\u200b E. \u200b\u751f\u7269\u200b\u8bc6\u522b\u200b\u4fe1\u606f\u200b \u200b\u6307\u7eb9\u200b\u548c\u200b\u58f0\u7eb9\u200b \u200b\u5426\u200b F. \u200b\u4e92\u8054\u7f51\u200b\u6216\u200b\u5176\u4ed6\u200b\u7c7b\u4f3c\u200b\u7f51\u7edc\u200b\u6d3b\u52a8\u200b \u200b\u6d4f\u89c8\u200b\u5386\u53f2\u200b\u3001\u200b\u641c\u7d22\u200b\u5386\u53f2\u200b\u3001\u200b\u5728\u7ebf\u200b\u884c\u4e3a\u200b\u3001\u200b\u5174\u8da3\u200b\u6570\u636e\u200b\u548c\u200b\u4e0e\u200b\u6211\u4eec\u200b\u548c\u200b\u5176\u4ed6\u200b\u7f51\u7ad9\u200b\u3001\u200b\u5e94\u7528\u7a0b\u5e8f\u200b\u3001\u200b\u7cfb\u7edf\u200b\u548c\u200b\u5e7f\u544a\u200b\u7684\u200b\u4e92\u52a8\u200b \u200b\u662f\u200b G. \u200b\u5730\u7406\u4f4d\u7f6e\u200b\u6570\u636e\u200b \u200b\u8bbe\u5907\u200b\u4f4d\u7f6e\u200b \u200b\u662f\u200b H. \u200b\u97f3\u9891\u200b\u3001\u200b\u7535\u5b50\u200b\u3001\u200b\u611f\u89c9\u200b\u6216\u200b\u7c7b\u4f3c\u200b\u4fe1\u606f\u200b \u200b\u5728\u200b\u6211\u4eec\u200b\u7684\u200b\u4e1a\u52a1\u200b\u6d3b\u52a8\u200b\u4e2d\u200b\u521b\u5efa\u200b\u7684\u200b\u56fe\u50cf\u200b\u548c\u200b\u97f3\u9891\u200b\u3001\u200b\u89c6\u9891\u200b\u6216\u200b\u901a\u8bdd\u5f55\u97f3\u200b \u200b\u5426\u200b I. \u200b\u4e0e\u200b\u804c\u4e1a\u200b\u76f8\u5173\u200b\u7684\u200b\u4fe1\u606f\u200b \u200b\u4e3a\u4e86\u200b\u5728\u200b\u4e1a\u52a1\u200b\u5c42\u9762\u200b\u63d0\u4f9b\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u800c\u200b\u6536\u96c6\u200b\u7684\u200b\u5546\u4e1a\u200b\u8054\u7cfb\u200b\u4fe1\u606f\u200b\u6216\u200b\u804c\u52a1\u200b\u540d\u79f0\u200b\u3001\u200b\u5de5\u4f5c\u200b\u5386\u53f2\u200b\u548c\u200b\u804c\u4e1a\u8d44\u683c\u200b \u200b\u5426\u200b J. \u200b\u6559\u80b2\u200b\u4fe1\u606f\u200b \u200b\u5b66\u751f\u200b\u8bb0\u5f55\u200b\u548c\u200b\u76ee\u5f55\u200b\u4fe1\u606f\u200b \u200b\u5426\u200b K. \u200b\u4ece\u200b\u6536\u96c6\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u4e2d\u200b\u63a8\u65ad\u51fa\u200b\u7684\u200b\u63a8\u8bba\u200b \u200b\u4ece\u200b\u4e0a\u8ff0\u200b\u4efb\u4f55\u200b\u6536\u96c6\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u4e2d\u200b\u63a8\u65ad\u51fa\u200b\u7684\u200b\u7528\u4e8e\u200b\u521b\u5efa\u200b\u4e2a\u4eba\u200b\u504f\u597d\u200b\u548c\u200b\u7279\u5f81\u200b\u7684\u200b\u6982\u51b5\u200b\u6216\u200b\u6458\u8981\u200b \u200b\u662f\u200b L. \u200b\u654f\u611f\u200b\u4e2a\u4eba\u4fe1\u606f\u200b \u200b\u5426\u200b

\u200b\u6211\u4eec\u200b\u8fd8\u200b\u53ef\u80fd\u200b\u5728\u200b\u60a8\u200b\u4e0e\u200b\u6211\u4eec\u200b\u4eb2\u81ea\u200b\u3001\u200b\u5728\u7ebf\u200b\u6216\u200b\u901a\u8fc7\u200b\u7535\u8bdd\u200b\u6216\u200b\u90ae\u4ef6\u200b\u4e0e\u200b\u6211\u4eec\u200b\u4e92\u52a8\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u6536\u96c6\u200b\u5176\u4ed6\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u5305\u62ec\u200b\uff1a

  • \u200b\u901a\u8fc7\u200b\u6211\u4eec\u200b\u7684\u200b\u5ba2\u6237\u200b\u652f\u6301\u200b\u6e20\u9053\u200b\u83b7\u5f97\u200b\u5e2e\u52a9\u200b\uff1b
  • \u200b\u53c2\u4e0e\u200b\u5ba2\u6237\u200b\u8c03\u67e5\u200b\u6216\u200b\u7ade\u8d5b\u200b\uff1b\u200b\u4ee5\u53ca\u200b
  • \u200b\u4fc3\u8fdb\u200b\u6211\u4eec\u200b\u670d\u52a1\u200b\u7684\u200b\u4ea4\u4ed8\u200b\u5e76\u200b\u56de\u5e94\u200b\u60a8\u200b\u7684\u200b\u67e5\u8be2\u200b\u3002

\u200b\u6211\u4eec\u200b\u5c06\u200b\u6839\u636e\u200b\u9700\u8981\u200b\u5728\u200b\u4ee5\u4e0b\u200b\u671f\u9650\u5185\u200b\u4f7f\u7528\u200b\u548c\u200b\u4fdd\u7559\u200b\u6240\u200b\u6536\u96c6\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u4ee5\u4fbf\u200b\u4e3a\u200b\u60a8\u200b\u63d0\u4f9b\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\uff0c\u200b\u5e76\u200b\u6839\u636e\u200b\u9700\u8981\u200b\u9075\u5b88\u200b\u6211\u4eec\u200b\u7684\u200b\u6cd5\u5f8b\u4e49\u52a1\u200b\u3001\u200b\u89e3\u51b3\u200b\u4e89\u8bae\u200b\u548c\u200b\u6267\u884c\u200b\u6211\u4eec\u200b\u7684\u200b\u534f\u8bae\u200b\uff1a

  • A \u200b\u7c7b\u200b\uff1a\u200b\u65e0\u9650\u671f\u200b
  • F \u200b\u7c7b\u200b\uff1a\u200b\u65e0\u9650\u671f\u200b
  • G \u200b\u7c7b\u200b\uff1a\u200b\u65e0\u9650\u671f\u200b
  • K \u200b\u7c7b\u200b\uff1a\u200b\u65e0\u9650\u671f\u200b
"},{"location":"zh/about/privacy/#2","title":"2. \u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u4ee5\u200b\u63d0\u4f9b\u200b\u3001\u200b\u6539\u5584\u200b\u548c\u200b\u7ba1\u7406\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\uff0c\u200b\u4e0e\u200b\u60a8\u200b\u6c9f\u901a\u200b\uff0c\u200b\u8fdb\u884c\u200b\u5b89\u5168\u200b\u548c\u200b\u9632\u200b\u6b3a\u8bc8\u200b\uff0c\u200b\u4ee5\u53ca\u200b\u9075\u5b88\u200b\u6cd5\u5f8b\u200b\u3002 \u200b\u6211\u4eec\u200b\u4e5f\u200b\u53ef\u80fd\u200b\u5728\u200b\u5f97\u5230\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u51fa\u4e8e\u200b\u5176\u4ed6\u200b\u76ee\u7684\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u51fa\u4e8e\u200b\u591a\u79cd\u200b\u539f\u56e0\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u8fd9\u200b\u53d6\u51b3\u4e8e\u200b\u60a8\u200b\u5982\u4f55\u200b\u4e0e\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u4e92\u52a8\u200b\uff0c\u200b\u5305\u62ec\u200b\uff1a

  • \u200b\u4fdd\u62a4\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u4f5c\u4e3a\u200b\u6211\u4eec\u200b\u4fdd\u6301\u200b\u670d\u52a1\u200b\u5b89\u5168\u200b\u7684\u200b\u52aa\u529b\u200b\u7684\u200b\u4e00\u90e8\u5206\u200b\uff0c\u200b\u5305\u62ec\u200b\u76d1\u63a7\u200b\u548c\u200b\u9884\u9632\u200b\u6b3a\u8bc8\u200b\u3002
  • \u200b\u8bc6\u522b\u200b\u7528\u6237\u200b\u8d8b\u52bf\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5904\u7406\u200b\u6709\u5173\u200b\u60a8\u200b\u5982\u4f55\u200b\u4f7f\u7528\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4ee5\u200b\u66f4\u597d\u200b\u5730\u200b\u4e86\u89e3\u200b\u5b83\u4eec\u200b\u7684\u200b\u4f7f\u7528\u200b\u60c5\u51b5\u200b\uff0c\u200b\u4ece\u800c\u200b\u6539\u8fdb\u200b\u5b83\u4eec\u200b\u3002
  • \u200b\u4fdd\u5b58\u200b\u6216\u200b\u4fdd\u62a4\u200b\u4e2a\u4eba\u200b\u7684\u200b\u91cd\u8981\u200b\u5229\u76ca\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5728\u200b\u5fc5\u8981\u200b\u65f6\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4ee5\u200b\u4fdd\u5b58\u200b\u6216\u200b\u4fdd\u62a4\u200b\u4e2a\u4eba\u200b\u7684\u200b\u91cd\u8981\u200b\u5229\u76ca\u200b\uff0c\u200b\u4f8b\u5982\u200b\u4e3a\u4e86\u200b\u9632\u6b62\u200b\u4f24\u5bb3\u200b\u3002
"},{"location":"zh/about/privacy/#3","title":"3. \u200b\u6211\u4eec\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u6709\u200b\u4ec0\u4e48\u200b\u6cd5\u5f8b\u4f9d\u636e\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u53ea\u6709\u200b\u5728\u200b\u6211\u4eec\u200b\u8ba4\u4e3a\u200b\u5fc5\u8981\u200b\u4e14\u200b\u6709\u200b\u6709\u6548\u200b\u7684\u200b\u6cd5\u5f8b\u200b\u7406\u7531\u200b\uff08\u200b\u5373\u200b\u6cd5\u5f8b\u4f9d\u636e\u200b\uff09\u200b\u65f6\u624d\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u5982\u200b\u4e0e\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u3001\u200b\u9075\u5b88\u200b\u6cd5\u5f8b\u200b\u3001\u200b\u63d0\u4f9b\u200b\u670d\u52a1\u200b\u7ed9\u200b\u60a8\u200b\u8fdb\u5165\u200b\u6216\u200b\u5c65\u884c\u200b\u6211\u4eec\u200b\u7684\u200b\u5408\u540c\u200b\u4e49\u52a1\u200b\u3001\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b\u6216\u200b\u6ee1\u8db3\u200b\u6211\u4eec\u200b\u5408\u6cd5\u200b\u7684\u200b\u4e1a\u52a1\u200b\u5229\u76ca\u200b\u3002

\u200b\u6b27\u76df\u200b\u901a\u7528\u200b\u6570\u636e\u4fdd\u62a4\u200b\u6761\u4f8b\u200b\uff08GDPR\uff09\u200b\u548c\u200b\u82f1\u56fd\u200bGDPR\u200b\u8981\u6c42\u200b\u6211\u4eec\u200b\u89e3\u91ca\u200b\u6211\u4eec\u200b\u4f9d\u9760\u200b\u7684\u200b\u6709\u6548\u200b\u6cd5\u5f8b\u4f9d\u636e\u200b\u4ee5\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002 \u200b\u56e0\u6b64\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f9d\u8d56\u200b\u4ee5\u4e0b\u200b\u6cd5\u5f8b\u4f9d\u636e\u200b\u6765\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1a

  • \u200b\u540c\u610f\u200b\u3002 \u200b\u5982\u679c\u200b\u60a8\u200b\u5df2\u200b\u7ed9\u200b\u6211\u4eec\u200b\u660e\u786e\u200b\u540c\u610f\u200b\u4f7f\u7528\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7528\u4e8e\u200b\u67d0\u4e2a\u200b\u7279\u5b9a\u200b\u76ee\u7684\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002 \u200b\u60a8\u200b\u6709\u6743\u200b\u968f\u65f6\u200b\u64a4\u56de\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u3002 \u200b\u4e86\u89e3\u200b\u66f4\u200b\u591a\u200b\u5173\u4e8e\u200b\u64a4\u56de\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u3002
  • \u200b\u5408\u6cd5\u5229\u76ca\u200b\u3002 \u200b\u5f53\u200b\u6211\u4eec\u200b\u8ba4\u4e3a\u200b\u51fa\u4e8e\u200b\u6211\u4eec\u200b\u5408\u6cd5\u200b\u7684\u200b\u4e1a\u52a1\u200b\u5229\u76ca\u200b\u6765\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u662f\u200b\u5408\u7406\u200b\u5fc5\u8981\u200b\u7684\u200b\uff0c\u200b\u5e76\u4e14\u200b\u8fd9\u4e9b\u200b\u5229\u76ca\u200b\u4e0d\u200b\u8d85\u8fc7\u200b\u60a8\u200b\u7684\u200b\u5229\u76ca\u200b\u548c\u200b\u57fa\u672c\u6743\u5229\u200b\u4e0e\u200b\u81ea\u7531\u200b\u65f6\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002 \u200b\u4f8b\u5982\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u7528\u4e8e\u200b\uff1a
    • \u200b\u5206\u6790\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u5982\u4f55\u200b\u88ab\u200b\u4f7f\u7528\u200b\uff0c\u200b\u4ee5\u4fbf\u200b\u6211\u4eec\u200b\u53ef\u4ee5\u200b\u6539\u8fdb\u200b\u5b83\u4eec\u200b\u4ee5\u200b\u5438\u5f15\u200b\u548c\u200b\u4fdd\u7559\u200b\u7528\u6237\u200b
    • \u200b\u8bca\u65ad\u200b\u95ee\u9898\u200b\u548c\u200b/\u200b\u6216\u200b\u9884\u9632\u200b\u6b3a\u8bc8\u200b\u6d3b\u52a8\u200b
  • \u200b\u6cd5\u5f8b\u4e49\u52a1\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5728\u200b\u6211\u4eec\u200b\u8ba4\u4e3a\u200b\u5fc5\u987b\u200b\u9075\u5b88\u200b\u6211\u4eec\u200b\u7684\u200b\u6cd5\u5f8b\u4e49\u52a1\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4f8b\u5982\u200b\u4e0e\u200b\u6267\u6cd5\u200b\u673a\u6784\u200b\u6216\u200b\u76d1\u7ba1\u200b\u673a\u6784\u200b\u5408\u4f5c\u200b\u3001\u200b\u884c\u4f7f\u200b\u6216\u200b\u634d\u536b\u200b\u6211\u4eec\u200b\u7684\u200b\u6cd5\u5f8b\u200b\u6743\u5229\u200b\uff0c\u200b\u6216\u200b\u5728\u200b\u6211\u4eec\u200b\u53c2\u4e0e\u200b\u7684\u200b\u8bc9\u8bbc\u200b\u4e2d\u200b\u62ab\u9732\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u4f5c\u4e3a\u200b\u8bc1\u636e\u200b\u3002
  • \u200b\u91cd\u8981\u200b\u5229\u76ca\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5728\u200b\u6211\u4eec\u200b\u8ba4\u4e3a\u200b\u5fc5\u987b\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u6216\u200b\u7b2c\u4e09\u65b9\u200b\u7684\u200b\u91cd\u8981\u200b\u5229\u76ca\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4f8b\u5982\u200b\u6d89\u53ca\u200b\u6f5c\u5728\u200b\u5a01\u80c1\u200b\u4efb\u4f55\u4eba\u200b\u7684\u200b\u5b89\u5168\u200b\u7684\u200b\u60c5\u51b5\u200b\u3002

\u200b\u5728\u200b\u52a0\u62ff\u5927\u200b\u5904\u7406\u200b\u7684\u200b\u540c\u610f\u200b

\u200b\u5982\u679c\u200b\u60a8\u200b\u4f4d\u4e8e\u200b\u52a0\u62ff\u5927\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u5728\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u4e0b\u200b\u5728\u200b\u67d0\u4e9b\u200b\u7279\u6b8a\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u65e0\u9700\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u5c31\u200b\u53ef\u4ee5\u200b\u5408\u6cd5\u200b\u5730\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u5305\u62ec\u200b\u4f8b\u5982\u200b\uff1a

  • \u200b\u5982\u679c\u200b\u6536\u96c6\u200b\u660e\u663e\u200b\u7b26\u5408\u200b\u4e2a\u4eba\u200b\u7684\u200b\u5229\u76ca\u200b\u4e14\u200b\u65e0\u6cd5\u200b\u53ca\u65f6\u200b\u83b7\u5f97\u200b\u540c\u610f\u200b
  • \u200b\u7528\u4e8e\u200b\u8c03\u67e5\u200b\u548c\u200b\u6b3a\u8bc8\u200b\u68c0\u6d4b\u200b\u4e0e\u200b\u9884\u9632\u200b
  • \u200b\u7528\u4e8e\u200b\u5546\u4e1a\u200b\u4ea4\u6613\u200b\uff0c\u200b\u524d\u63d0\u200b\u662f\u200b\u6ee1\u8db3\u200b\u67d0\u4e9b\u200b\u6761\u4ef6\u200b
  • \u200b\u5982\u679c\u200b\u4fe1\u606f\u200b\u5305\u542b\u200b\u5728\u200b\u8bc1\u4eba\u200b\u58f0\u660e\u200b\u4e2d\u200b\uff0c\u200b\u4e14\u200b\u6536\u96c6\u200b\u5bf9\u4e8e\u200b\u8bc4\u4f30\u200b\u3001\u200b\u5904\u7406\u200b\u6216\u200b\u89e3\u51b3\u200b\u4fdd\u9669\u200b\u7d22\u8d54\u200b\u662f\u200b\u5fc5\u8981\u200b\u7684\u200b
  • \u200b\u7528\u4e8e\u200b\u8bc6\u522b\u200b\u53d7\u4f24\u200b\u3001\u200b\u751f\u75c5\u200b\u6216\u200b\u5df2\u6545\u200b\u4eba\u58eb\u200b\u5e76\u200b\u4e0e\u200b\u8fd1\u4eb2\u200b\u6c9f\u901a\u200b
  • \u200b\u5982\u679c\u200b\u6211\u4eec\u200b\u6709\u200b\u5408\u7406\u200b\u7684\u200b\u7406\u7531\u200b\u76f8\u4fe1\u200b\u67d0\u4e2a\u200b\u4eba\u200b\u5df2\u7ecf\u200b\u3001\u200b\u6b63\u5728\u200b\u6216\u200b\u53ef\u80fd\u200b\u6210\u4e3a\u200b\u91d1\u878d\u200b\u6ee5\u7528\u200b\u7684\u200b\u53d7\u5bb3\u8005\u200b
  • \u200b\u5982\u679c\u200b\u5408\u7406\u200b\u9884\u671f\u200b\u901a\u8fc7\u200b\u5f81\u5f97\u200b\u540c\u610f\u200b\u4ee5\u200b\u6536\u96c6\u200b\u548c\u200b\u4f7f\u7528\u200b\u4fe1\u606f\u200b\u4f1a\u200b\u635f\u5bb3\u200b\u4fe1\u606f\u200b\u7684\u200b\u53ef\u7528\u6027\u200b\u6216\u200b\u51c6\u786e\u6027\u200b\uff0c\u200b\u5e76\u4e14\u200b\u6536\u96c6\u200b\u5bf9\u4e8e\u200b\u8c03\u67e5\u200b\u8fdd\u53cd\u200b\u534f\u8bae\u200b\u6216\u200b\u8fdd\u53cd\u200b\u52a0\u62ff\u5927\u200b\u6216\u7701\u200b\u6cd5\u5f8b\u200b\u7684\u200b\u76ee\u7684\u200b\u662f\u200b\u5408\u7406\u200b\u7684\u200b
  • \u200b\u5982\u679c\u200b\u62ab\u9732\u200b\u662f\u200b\u4e3a\u4e86\u200b\u9075\u5b88\u200b\u4f20\u7968\u200b\u3001\u200b\u641c\u67e5\u200b\u4ee4\u200b\u3001\u200b\u6cd5\u9662\u200b\u547d\u4ee4\u200b\u6216\u200b\u4e0e\u200b\u8bb0\u5f55\u200b\u751f\u4ea7\u200b\u76f8\u5173\u200b\u7684\u200b\u6cd5\u9662\u200b\u89c4\u5219\u200b
  • \u200b\u5982\u679c\u200b\u4fe1\u606f\u200b\u662f\u200b\u7531\u200b\u4e2a\u4eba\u200b\u5728\u200b\u5176\u200b\u5c31\u4e1a\u200b\u3001\u200b\u4e1a\u52a1\u200b\u6216\u200b\u4e13\u4e1a\u200b\u8fc7\u7a0b\u200b\u4e2d\u200b\u4ea7\u751f\u200b\u7684\u200b\uff0c\u200b\u5e76\u4e14\u200b\u6536\u96c6\u200b\u4e0e\u200b\u4fe1\u606f\u200b\u4ea7\u751f\u200b\u7684\u200b\u76ee\u7684\u200b\u4e00\u81f4\u200b
  • \u200b\u5982\u679c\u200b\u6536\u96c6\u200b\u4ec5\u200b\u7528\u4e8e\u200b\u65b0\u95fb\u200b\u3001\u200b\u827a\u672f\u200b\u6216\u200b\u6587\u5b66\u200b\u76ee\u7684\u200b
  • \u200b\u5982\u679c\u200b\u4fe1\u606f\u200b\u662f\u200b\u516c\u5f00\u200b\u53ef\u7528\u200b\u7684\u200b\uff0c\u200b\u5e76\u4e14\u200b\u901a\u8fc7\u200b\u89c4\u5b9a\u200b\u6307\u5b9a\u200b
"},{"location":"zh/about/privacy/#4","title":"4. \u200b\u6211\u4eec\u200b\u4f55\u65f6\u200b\u4ee5\u53ca\u200b\u4e0e\u200b\u8c01\u200b\u5171\u4eab\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u5728\u200b\u672c\u8282\u200b\u63cf\u8ff0\u200b\u7684\u200b\u7279\u5b9a\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u548c\u200b/\u200b\u6216\u200b\u4e0e\u200b\u4ee5\u4e0b\u200b\u7b2c\u4e09\u65b9\u200b\u5171\u4eab\u200b\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u5c06\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7528\u4e8e\u200b\u6211\u4eec\u200b\u7684\u200b\u4e1a\u52a1\u200b\u76ee\u7684\u200b\uff0c\u200b\u5982\u200b\u8fdb\u884c\u200b\u5185\u90e8\u200b\u7814\u7a76\u200b\u4ee5\u200b\u8fdb\u884c\u200b\u6280\u672f\u5f00\u53d1\u200b\u548c\u200b\u5c55\u793a\u200b\u3002 \u200b\u8fd9\u200b\u4e0d\u200b\u88ab\u200b\u89c6\u4e3a\u200b\u201c\u200b\u51fa\u552e\u200b\u201d\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

\u200b\u4f9b\u5e94\u5546\u200b\u3001\u200b\u987e\u95ee\u200b\u548c\u200b\u5176\u4ed6\u200b\u7b2c\u4e09\u65b9\u200b\u670d\u52a1\u63d0\u4f9b\u5546\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u4e0e\u200b\u4e3a\u200b\u6211\u4eec\u200b\u670d\u52a1\u200b\u6216\u200b\u4ee3\u8868\u200b\u6211\u4eec\u200b\u5de5\u4f5c\u200b\u5e76\u200b\u9700\u8981\u200b\u8bbf\u95ee\u200b\u6b64\u7c7b\u200b\u4fe1\u606f\u200b\u4ee5\u200b\u6267\u884c\u200b\u8be5\u200b\u5de5\u4f5c\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u4f9b\u5e94\u5546\u200b\u3001\u200b\u670d\u52a1\u63d0\u4f9b\u5546\u200b\u3001\u200b\u627f\u5305\u5546\u200b\u6216\u200b\u4ee3\u7406\u200b\uff08\u201c\u200b\u7b2c\u4e09\u65b9\u200b\u201d\uff09\u200b\u5171\u4eab\u200b\u60a8\u200b\u7684\u200b\u6570\u636e\u200b\u3002 \u200b\u6211\u4eec\u200b\u4e0e\u200b\u6211\u4eec\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u7b7e\u8ba2\u200b\u4e86\u200b\u5408\u540c\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u5408\u540c\u200b\u65e8\u5728\u200b\u5e2e\u52a9\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002 \u200b\u8fd9\u200b\u610f\u5473\u7740\u200b\u4ed6\u4eec\u200b\u4e0d\u80fd\u200b\u5728\u200b\u672a\u7ecf\u200b\u6211\u4eec\u200b\u6307\u793a\u200b\u7684\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u505a\u200b\u4efb\u4f55\u200b\u4e8b\u60c5\u200b\u3002 \u200b\u4ed6\u4eec\u200b\u4e5f\u200b\u4e0d\u4f1a\u200b\u4e0e\u200b\u6211\u4eec\u200b\u4ee5\u5916\u200b\u7684\u200b\u4efb\u4f55\u200b\u7ec4\u7ec7\u200b\u5171\u4eab\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002 \u200b\u4ed6\u4eec\u200b\u8fd8\u200b\u627f\u8bfa\u200b\u4fdd\u62a4\u200b\u4ed6\u4eec\u200b\u4ee3\u8868\u200b\u6211\u4eec\u200b\u6301\u6709\u200b\u7684\u200b\u6570\u636e\u200b\u5e76\u200b\u6309\u7167\u200b\u6211\u4eec\u200b\u7684\u200b\u6307\u793a\u200b\u4fdd\u7559\u200b\u8be5\u200b\u6570\u636e\u200b\u3002

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u5171\u4eab\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u5982\u4e0b\u200b\uff1a

  • \u200b\u5e7f\u544a\u200b\u3001\u200b\u76f4\u9500\u200b\u548c\u200b\u6f5c\u5728\u200b\u5ba2\u6237\u200b\u751f\u6210\u200b
    • Google AdSense
  • \u200b\u4e91\u200b\u8ba1\u7b97\u200b\u670d\u52a1\u200b
    • Microsoft Azure
    • Amazon Web Services (AWS)
    • Google Cloud Platform (GCP)
  • \u200b\u901a\u4fe1\u200b\u548c\u200b\u5185\u5bb9\u200b\u4ea4\u4ed8\u200b\u7f51\u7edc\u200b (CDN) \u200b\u670d\u52a1\u200b
    • Cloudflare
  • \u200b\u5185\u5bb9\u200b\u4f18\u5316\u200b
    • Google\u200b\u7ad9\u70b9\u200b\u641c\u7d22\u200b
    • Google\u200b\u5b57\u4f53\u200b
  • \u200b\u529f\u80fd\u200b\u548c\u200b\u57fa\u7840\u8bbe\u65bd\u200b\u4f18\u5316\u200b
    • GitHub\u200b\u9875\u9762\u200b
  • \u200b\u7528\u6237\u200b\u8bc4\u8bba\u200b\u548c\u200b\u8bba\u575b\u200b
    • Disqus
    • GitHub\u200b\u8bae\u9898\u200b
    • GitHub\u200b\u8ba8\u8bba\u200b
  • \u200b\u7f51\u7edc\u200b\u548c\u200b\u79fb\u52a8\u200b\u5206\u6790\u200b
    • Google Analytics

\u200b\u6211\u4eec\u200b\u8fd8\u200b\u53ef\u80fd\u200b\u9700\u8981\u200b\u5728\u200b\u4ee5\u4e0b\u200b\u60c5\u51b5\u200b\u4e0b\u200b\u5171\u4eab\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1a

  • \u200b\u4e1a\u52a1\u200b\u8f6c\u79fb\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u5728\u200b\u8fdb\u884c\u200b\u4efb\u4f55\u200b\u5e76\u8d2d\u200b\u3001\u200b\u51fa\u552e\u200b\u516c\u53f8\u200b\u8d44\u4ea7\u200b\u3001\u200b\u878d\u8d44\u200b\u6216\u200b\u6536\u8d2d\u200b\u6211\u4eec\u200b\u5168\u90e8\u200b\u6216\u200b\u90e8\u5206\u200b\u4e1a\u52a1\u200b\u7684\u200b\u8c08\u5224\u200b\u4e2d\u200b\u5171\u4eab\u200b\u6216\u200b\u8f6c\u8ba9\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002

\u200b\u8fc7\u53bb\u200b\u5341\u4e8c\u200b\uff0812\uff09\u200b\u4e2a\u200b\u6708\u200b\u6211\u4eec\u200b\u51fa\u4e8e\u200b\u4e1a\u52a1\u200b\u76ee\u7684\u200b\u62ab\u9732\u200b\u4e86\u200b\u4ee5\u4e0b\u200b\u7c7b\u522b\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff1a

\u200b\u65e0\u200b

\u200b\u8fc7\u53bb\u200b\u5341\u4e8c\u200b\uff0812\uff09\u200b\u4e2a\u200b\u6708\u200b\u6211\u4eec\u200b\u51fa\u552e\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u7c7b\u522b\u200b\uff1a

\u200b\u65e0\u200b

\u200b\u8fc7\u53bb\u200b\u5341\u4e8c\u200b\uff0812\uff09\u200b\u4e2a\u200b\u6708\u200b\u6211\u4eec\u200b\u4e0e\u200b\u4e4b\u200b\u5171\u4eab\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u7c7b\u522b\u200b\uff1a

  • \u200b\u7f51\u7edc\u200b\u548c\u200b\u79fb\u52a8\u200b\u5206\u6790\u200b
    • Google Analytics
"},{"location":"zh/about/privacy/#5-cookies","title":"5. \u200b\u6211\u4eec\u200b\u662f\u5426\u200b\u4f7f\u7528\u200bcookies\u200b\u548c\u200b\u5176\u4ed6\u200b\u8ddf\u8e2a\u200b\u6280\u672f\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f7f\u7528\u200bcookies\u200b\u548c\u200b\u5176\u4ed6\u200b\u8ddf\u8e2a\u200b\u6280\u672f\u200b\u6765\u200b\u6536\u96c6\u200b\u548c\u200b\u5b58\u50a8\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u8fd8\u200b\u5141\u8bb8\u200b\u7b2c\u4e09\u65b9\u200b\u548c\u200b\u670d\u52a1\u63d0\u4f9b\u5546\u200b\u5728\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u4e2d\u200b\u4f7f\u7528\u200b\u5728\u7ebf\u200b\u8ddf\u8e2a\u200b\u6280\u672f\u200b\u7528\u4e8e\u200b\u5206\u6790\u200b\u548c\u200b\u5e7f\u544a\u200b\uff0c\u200b\u5305\u62ec\u200b\u5e2e\u52a9\u200b\u7ba1\u7406\u200b\u548c\u200b\u5c55\u793a\u200b\u5e7f\u544a\u200b\uff0c\u200b\u6839\u636e\u200b\u60a8\u200b\u7684\u200b\u5174\u8da3\u200b\u5b9a\u5236\u200b\u5e7f\u544a\u200b\uff0c\u200b\u6216\u200b\u53d1\u9001\u200b\u9057\u5f03\u200b\u8d2d\u7269\u8f66\u200b\u63d0\u9192\u200b\uff08\u200b\u53d6\u51b3\u4e8e\u200b\u60a8\u200b\u7684\u200b\u6c9f\u901a\u200b\u504f\u597d\u200b\uff09\u3002 \u200b\u8fd9\u4e9b\u200b\u7b2c\u4e09\u65b9\u200b\u548c\u200b\u670d\u52a1\u63d0\u4f9b\u5546\u200b\u4f7f\u7528\u200b\u4ed6\u4eec\u200b\u7684\u200b\u6280\u672f\u200b\u4e3a\u200b\u60a8\u200b\u63d0\u4f9b\u200b\u5b9a\u5236\u200b\u7684\u200b\u4ea7\u54c1\u200b\u548c\u200b\u670d\u52a1\u200b\u5e7f\u544a\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u5e7f\u544a\u200b\u53ef\u80fd\u200b\u51fa\u73b0\u200b\u5728\u200b\u6211\u4eec\u200b\u7684\u200b\u670d\u52a1\u200b\u6216\u200b\u5176\u4ed6\u200b\u7f51\u7ad9\u200b\u4e0a\u200b\u3002

\u200b\u5728\u200b\u9002\u7528\u200b\u7684\u200b\u7f8e\u56fd\u200b\u5dde\u200b\u6cd5\u5f8b\u200b\u4e0b\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u5728\u7ebf\u200b\u8ddf\u8e2a\u200b\u6280\u672f\u200b\u88ab\u200b\u89c6\u4e3a\u200b\u201c\u200b\u9500\u552e\u200b\u201d/\u201c\u200b\u5206\u4eab\u200b\u201d\uff08\u200b\u5305\u62ec\u200b\u76ee\u6807\u200b\u5e7f\u544a\u200b\uff0c\u200b\u6839\u636e\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u5b9a\u4e49\u200b\uff09\u200b\u7684\u200b\u7a0b\u5ea6\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u901a\u8fc7\u200b\u70b9\u51fb\u200b\u672c\u9875\u200b\u9876\u90e8\u200b\u6216\u200b\u4e0b\u9762\u200b\u7684\u200b\u6309\u94ae\u200b\u6765\u200b\u9009\u62e9\u200b\u9000\u51fa\u200b\u8fd9\u4e9b\u200b\u5728\u7ebf\u200b\u8ddf\u8e2a\u200b\u6280\u672f\u200b\uff1a

\u200b\u9690\u79c1\u200b\u63a7\u5236\u200b

"},{"location":"zh/about/privacy/#google-analytics","title":"Google Analytics","text":"

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u4e0e\u200bGoogle Analytics\u200b\u5171\u4eab\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u4ee5\u200b\u8ddf\u8e2a\u200b\u548c\u200b\u5206\u6790\u200b\u670d\u52a1\u200b\u7684\u200b\u4f7f\u7528\u200b\u60c5\u51b5\u200b\u3002 \u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f7f\u7528\u200b\u7684\u200bGoogle Analytics\u200b\u5e7f\u544a\u200b\u529f\u80fd\u200b\u5305\u62ec\u200b\uff1a

  • Google Analytics\u200b\u7684\u200b\u518d\u200b\u8425\u9500\u200b
  • Google Display Network\u200b\u5370\u8c61\u200b\u62a5\u544a\u200b
  • Google Analytics\u200b\u4eba\u53e3\u7edf\u8ba1\u200b\u548c\u200b\u5174\u8da3\u200b\u62a5\u544a\u200b

\u200b\u8981\u200b\u9009\u62e9\u200b\u9000\u51fa\u200b\u5728\u200b\u670d\u52a1\u200b\u4e2d\u200b\u901a\u8fc7\u200bGoogle Analytics\u200b\u8ddf\u8e2a\u200b\u60a8\u200b\uff0c\u200b\u8bf7\u200b\u8bbf\u95ee\u200bhttps://tools.google.com/dlpage/gaoptout\u3002

\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u901a\u8fc7\u200b\u5e7f\u544a\u200b\u8bbe\u7f6e\u200b\u548c\u200b\u79fb\u52a8\u200b\u5e94\u7528\u200b\u7684\u200b\u5e7f\u544a\u200b\u8bbe\u7f6e\u200b\u6765\u200b\u9009\u62e9\u200b\u9000\u51fa\u200bGoogle Analytics\u200b\u5e7f\u544a\u200b\u529f\u80fd\u200b\u3002

\u200b\u5176\u4ed6\u200b\u9000\u51fa\u200b\u65b9\u5f0f\u200b\u5305\u62ec\u200bhttp://optout.networkadvertising.org/\u200b\u548c\u200bhttp://www.networkadvertising.org/mobile-choice\u3002

\u200b\u6709\u5173\u200bGoogle\u200b\u9690\u79c1\u200b\u505a\u6cd5\u200b\u7684\u200b\u66f4\u200b\u591a\u200b\u4fe1\u606f\u200b\uff0c\u200b\u8bf7\u200b\u8bbf\u95ee\u200bGoogle\u200b\u9690\u79c1\u200b\u4e0e\u200b\u6761\u6b3e\u200b\u3002

"},{"location":"zh/about/privacy/#6","title":"6. \u200b\u6211\u4eec\u200b\u4fdd\u7559\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u591a\u4e45\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u5c06\u200b\u6839\u636e\u200b\u672c\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u4e2d\u200b\u6982\u8ff0\u200b\u7684\u200b\u76ee\u7684\u200b\u4fdd\u7559\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u9664\u975e\u200b\u6cd5\u5f8b\u200b\u53e6\u6709\u200b\u8981\u6c42\u200b\u3002

\u200b\u6211\u4eec\u200b\u53ea\u4f1a\u200b\u5728\u200b\u672c\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u4e2d\u200b\u6982\u8ff0\u200b\u7684\u200b\u76ee\u7684\u200b\u6240\u200b\u9700\u200b\u7684\u200b\u65f6\u95f4\u200b\u5185\u200b\u4fdd\u7559\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u9664\u975e\u200b\u6cd5\u5f8b\u200b\u8981\u6c42\u200b\u6216\u200b\u5141\u8bb8\u200b\u66f4\u957f\u200b\u7684\u200b\u4fdd\u7559\u200b\u671f\u200b\uff08\u200b\u5982\u200b\u7a0e\u52a1\u200b\u3001\u200b\u4f1a\u8ba1\u200b\u6216\u200b\u5176\u4ed6\u200b\u6cd5\u5f8b\u200b\u8981\u6c42\u200b\uff09\u3002

\u200b\u5f53\u200b\u6211\u4eec\u200b\u6ca1\u6709\u200b\u6301\u7eed\u200b\u7684\u200b\u5408\u6cd5\u200b\u4e1a\u52a1\u200b\u9700\u8981\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u65f6\u200b\uff0c\u200b\u6211\u4eec\u200b\u5c06\u200b\u5220\u9664\u200b\u6216\u200b\u533f\u540d\u200b\u5316\u5b83\u200b\uff0c\u200b\u6216\u8005\u200b\uff0c\u200b\u5982\u679c\u200b\u8fd9\u200b\u4e0d\u200b\u53ef\u80fd\u200b\uff08\u200b\u4f8b\u5982\u200b\uff0c\u200b\u56e0\u4e3a\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u5df2\u200b\u5b58\u50a8\u200b\u5728\u200b\u5907\u4efd\u200b\u6863\u6848\u200b\u4e2d\u200b\uff09\uff0c\u200b\u90a3\u4e48\u200b\u6211\u4eec\u200b\u5c06\u200b\u5b89\u5168\u200b\u5730\u200b\u5b58\u50a8\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u5e76\u200b\u5c06\u200b\u5176\u200b\u4e0e\u200b\u4efb\u4f55\u200b\u8fdb\u4e00\u6b65\u200b\u5904\u7406\u200b\u9694\u79bb\u200b\uff0c\u200b\u76f4\u5230\u200b\u5220\u9664\u200b\u6210\u4e3a\u200b\u53ef\u80fd\u200b\u3002

"},{"location":"zh/about/privacy/#7","title":"7. \u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u4fdd\u6301\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u5b89\u5168\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u65e8\u5728\u200b\u901a\u8fc7\u200b\u4e00\u7cfb\u5217\u200b\u7ec4\u7ec7\u200b\u548c\u200b\u6280\u672f\u200b\u5b89\u5168\u63aa\u65bd\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u3002

\u200b\u6211\u4eec\u200b\u5df2\u200b\u5b9e\u65bd\u200b\u9002\u5f53\u200b\u7684\u200b\u6280\u672f\u200b\u548c\u200b\u7ec4\u7ec7\u200b\u5b89\u5168\u63aa\u65bd\u200b\uff0c\u200b\u65e8\u5728\u200b\u4fdd\u62a4\u200b\u6211\u4eec\u200b\u5904\u7406\u200b\u7684\u200b\u4efb\u4f55\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7684\u200b\u5b89\u5168\u200b\u3002 \u200b\u7136\u800c\u200b\uff0c\u200b\u5c3d\u7ba1\u200b\u6211\u4eec\u200b\u91c7\u53d6\u200b\u4e86\u200b\u4fdd\u969c\u200b\u63aa\u65bd\u200b\u5e76\u200b\u52aa\u529b\u200b\u786e\u4fdd\u60a8\u200b\u7684\u200b\u4fe1\u606f\u5b89\u5168\u200b\uff0c\u200b\u4efb\u4f55\u200b\u901a\u8fc7\u200b\u4e92\u8054\u7f51\u200b\u7684\u200b\u7535\u5b50\u200b\u4f20\u8f93\u200b\u6216\u200b\u4fe1\u606f\u200b\u5b58\u50a8\u6280\u672f\u200b\u90fd\u200b\u65e0\u6cd5\u200b\u4fdd\u8bc1\u200b\u662f\u200b100%\u200b\u5b89\u5168\u200b\u7684\u200b\uff0c\u200b\u56e0\u6b64\u200b\u6211\u4eec\u200b\u65e0\u6cd5\u200b\u627f\u8bfa\u200b\u6216\u200b\u4fdd\u8bc1\u200b\u9ed1\u5ba2\u200b\u3001\u200b\u7f51\u7edc\u200b\u72af\u7f6a\u5206\u5b50\u200b\u6216\u200b\u5176\u4ed6\u200b\u672a\u7ecf\u200b\u6388\u6743\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u4e0d\u4f1a\u200b\u7834\u574f\u200b\u6211\u4eec\u200b\u7684\u200b\u5b89\u5168\u63aa\u65bd\u200b\u5e76\u200b\u4e0d\u200b\u5f53\u5730\u200b\u6536\u96c6\u200b\u3001\u200b\u8bbf\u95ee\u200b\u3001\u200b\u7a83\u53d6\u200b\u6216\u200b\u4fee\u6539\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002 \u200b\u5c3d\u7ba1\u200b\u6211\u4eec\u200b\u5c06\u200b\u5c3d\u200b\u6700\u5927\u200b\u52aa\u529b\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u5230\u200b\u6211\u4eec\u200b\u670d\u52a1\u200b\u7684\u200b\u4f20\u8f93\u200b\u548c\u200b\u4ece\u200b\u6211\u4eec\u200b\u670d\u52a1\u200b\u7684\u200b\u4f20\u8f93\u200b\u4ecd\u7136\u200b\u662f\u200b\u60a8\u200b\u81ea\u5df1\u200b\u7684\u200b\u98ce\u9669\u200b\u3002 \u200b\u60a8\u200b\u5e94\u8be5\u200b\u53ea\u200b\u5728\u200b\u5b89\u5168\u200b\u7684\u200b\u73af\u5883\u200b\u4e2d\u200b\u8bbf\u95ee\u200b\u670d\u52a1\u200b\u3002

"},{"location":"zh/about/privacy/#8","title":"8. \u200b\u60a8\u200b\u6709\u200b\u54ea\u4e9b\u200b\u9690\u79c1\u200b\u6743\u5229\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u6211\u4eec\u200b\u52aa\u529b\u200b\u5728\u200b\u6cd5\u5f8b\u200b\u5141\u8bb8\u200b\u7684\u200b\u6700\u5927\u200b\u8303\u56f4\u200b\u5185\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u9690\u79c1\u200b\u6743\u5229\u200b\u548c\u200b\u9009\u62e9\u200b\u3002

\u200b\u60a8\u200b\u5728\u200b\u67d0\u4e9b\u200b\u6570\u636e\u4fdd\u62a4\u200b\u6cd5\u4e0b\u200b\u6709\u200b\u6743\u5229\u200b\u3002 \u200b\u7136\u800c\u200b\uff0c\u200b\u8fd9\u4e9b\u200b\u6743\u5229\u200b\u4e0d\u662f\u200b\u7edd\u5bf9\u200b\u7684\u200b\uff0c\u200b\u5728\u200b\u67d0\u4e9b\u200b\u60c5\u51b5\u200b\u4e0b\u200b\uff0c\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u6839\u636e\u200b\u6cd5\u5f8b\u200b\u62d2\u7edd\u200b\u60a8\u200b\u7684\u200b\u8bf7\u6c42\u200b\u3002 \u200b\u8fd9\u4e9b\u200b\u6743\u5229\u200b\u5305\u62ec\u200b\uff1a

  • \u200b\u77e5\u60c5\u6743\u200b \u200b\u6211\u4eec\u200b\u662f\u5426\u200b\u6b63\u5728\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b
  • \u200b\u8bbf\u95ee\u200b\u6743\u200b \u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b
  • \u200b\u66f4\u6b63\u200b\u6743\u200b \u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\u4e2d\u200b\u7684\u200b\u4e0d\u200b\u51c6\u786e\u200b\u4fe1\u606f\u200b
  • \u200b\u8bf7\u6c42\u200b\u5220\u9664\u200b\u6743\u200b \u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b
  • \u200b\u83b7\u53d6\u200b\u526f\u672c\u200b\u6743\u200b \u200b\u60a8\u200b\u4ee5\u524d\u200b\u4e0e\u200b\u6211\u4eec\u200b\u5171\u4eab\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b
  • \u200b\u53cd\u200b\u6b67\u89c6\u200b\u6743\u200b \u200b\u9488\u5bf9\u200b\u60a8\u200b\u884c\u4f7f\u200b\u60a8\u200b\u7684\u200b\u6743\u5229\u200b
  • \u200b\u9009\u62e9\u200b\u9000\u51fa\u200b\u6743\u200b
    • \u200b\u5982\u679c\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\u7528\u4e8e\u200b\u76ee\u6807\u200b\u5e7f\u544a\u200b\uff08\u200b\u6216\u200b\u6839\u636e\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u5b9a\u4e49\u200b\u7684\u200b\u201c\u200b\u5206\u4eab\u200b\u201d\uff09\uff0c\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\u7684\u200b\u9500\u552e\u200b\uff0c\u200b\u6216\u200b\u4fc3\u8fdb\u200b\u5bf9\u200b\u60a8\u200b\u4ea7\u751f\u200b\u6cd5\u5f8b\u200b\u6216\u200b\u7c7b\u4f3c\u200b\u91cd\u5927\u200b\u6548\u679c\u200b\u7684\u200b\u51b3\u7b56\u200b\uff08\u201c\u200b\u5206\u6790\u200b\u201d\uff09\u200b\u7684\u200b\u5206\u6790\u200b
    • \u200b\u6536\u96c6\u200b\u901a\u8fc7\u200b\u8bed\u97f3\u200b\u6216\u200b\u9762\u90e8\u200b\u8bc6\u522b\u200b\u529f\u80fd\u200b\u64cd\u4f5c\u200b\u6536\u96c6\u200b\u7684\u200b\u654f\u611f\u6570\u636e\u200b\u548c\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b
  • \u200b\u83b7\u53d6\u200b\u6743\u200b
    • \u200b\u5411\u200b\u6211\u4eec\u200b\u62ab\u9732\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\u7684\u200b\u7b2c\u4e09\u65b9\u200b\u7c7b\u522b\u200b\u7684\u200b\u5217\u8868\u200b
    • \u200b\u5411\u200b\u6211\u4eec\u200b\u62ab\u9732\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\u7684\u200b\u7279\u5b9a\u200b\u7b2c\u4e09\u65b9\u200b\u7684\u200b\u5217\u8868\u200b
  • \u200b\u9650\u5236\u200b\u4f7f\u7528\u200b\u548c\u200b\u62ab\u9732\u200b\u6743\u200b \u200b\u654f\u611f\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b
"},{"location":"zh/about/privacy/#_5","title":"\u5982\u4f55\u200b\u884c\u4f7f\u200b\u60a8\u200b\u7684\u200b\u6743\u5229","text":"

\u200b\u60a8\u200b\u51e0\u4e4e\u200b\u4e0d\u200b\u53ef\u80fd\u200b\u884c\u4f7f\u200b\u4e0a\u8ff0\u200b\u6743\u5229\u200b\uff0c\u200b\u56e0\u4e3a\u200b\u6211\u4eec\u200b\u4e0d\u200b\u4ece\u200b\u60a8\u200b\u90a3\u91cc\u200b\u6536\u96c6\u200b\u4efb\u4f55\u200b\u53ef\u200b\u8bc6\u522b\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\u3002

\u200b\u6211\u4eec\u200b\u65e0\u6cd5\u200b\u56de\u590d\u200b\u548c\u200b\u91c7\u53d6\u200b\u6570\u636e\u200b\u4e3b\u4f53\u200b\u8bbf\u95ee\u200b\u8bf7\u6c42\u200b\uff0c\u200b\u56e0\u4e3a\u200b\u6211\u4eec\u200b\u4e0d\u200b\u4fdd\u5b58\u200b\u4efb\u4f55\u200b\u53ef\u200b\u8bc6\u522b\u200b\u7684\u200b\u5173\u4e8e\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\uff0c\u200b\u6211\u4eec\u200b\u5c06\u200b\u65e0\u6cd5\u200b\u9a8c\u8bc1\u200b\u60a8\u200b\u7684\u200b\u8eab\u4efd\u200b\u3002

\u200b\u5982\u679c\u200b\u60a8\u200b\u8ba4\u4e3a\u200b\u6211\u4eec\u200b\u975e\u6cd5\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u8054\u7cfb\u200b\u60a8\u200b\u6240\u5728\u200b\u7ba1\u8f96\u533a\u200b\u7684\u200b\u76f8\u5173\u200b\u6570\u636e\u4fdd\u62a4\u200b\u76d1\u7ba1\u200b\u673a\u6784\u200b\u3001\u200b\u5dde\u200b\u603b\u200b\u68c0\u5bdf\u957f\u200b\u6216\u200b\u5176\u4ed6\u200b\u6709\u6743\u200b\u673a\u6784\u200b\u3002

\u200b\u5c45\u4f4f\u5730\u200b \u200b\u673a\u6784\u200b \u200b\u6b27\u6d32\u200b\u7ecf\u6d4e\u533a\u200b \u200b\u6210\u5458\u56fd\u200b\u7684\u200b\u6570\u636e\u4fdd\u62a4\u200b\u76d1\u7763\u673a\u6784\u200b \u200b\u82f1\u56fd\u200b \u200b\u4fe1\u606f\u200b\u4e13\u5458\u200b\u529e\u516c\u5ba4\u200b \u200b\u6fb3\u5927\u5229\u4e9a\u200b \u200b\u6fb3\u5927\u5229\u4e9a\u200b\u4fe1\u606f\u200b\u4e13\u5458\u200b\u529e\u516c\u5ba4\u200b \u200b\u65b0\u897f\u5170\u200b \u200b\u65b0\u897f\u5170\u200b\u9690\u79c1\u200b\u4e13\u5458\u200b\u529e\u516c\u5ba4\u200b \u200b\u52a0\u62ff\u5927\u200b \u200b\u52a0\u62ff\u5927\u200b\u9690\u79c1\u200b\u4e13\u5458\u200b\u529e\u516c\u5ba4\u200b \u200b\u7f8e\u56fd\u200b\u52a0\u5229\u798f\u5c3c\u4e9a\u5dde\u200b \u200b\u52a0\u5229\u798f\u5c3c\u4e9a\u200b\u9690\u79c1\u200b\u4fdd\u62a4\u200b\u673a\u6784\u200b \u200b\u745e\u58eb\u200b \u200b\u8054\u90a6\u200b\u6570\u636e\u4fdd\u62a4\u200b\u548c\u200b\u4fe1\u606f\u200b\u4e13\u5458\u200b \u200b\u5357\u975e\u200b \u200b\u4fe1\u606f\u200b\u76d1\u7ba1\u200b\u673a\u6784"},{"location":"zh/about/privacy/#_6","title":"\u64a4\u56de\u200b\u60a8\u200b\u7684\u200b\u540c\u610f","text":"

\u200b\u5982\u679c\u200b\u6211\u4eec\u200b\u4f9d\u8d56\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u6765\u200b\u5904\u7406\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\uff0c\u200b\u8fd9\u200b\u53ef\u80fd\u200b\u662f\u200b\u660e\u786e\u200b\u7684\u200b\u548c\u200b/\u200b\u6216\u200b\u6697\u793a\u200b\u7684\u200b\u540c\u610f\u200b\uff0c\u200b\u53d6\u51b3\u4e8e\u200b\u9002\u7528\u6cd5\u5f8b\u200b\uff0c\u200b\u60a8\u200b\u6709\u6743\u200b\u968f\u65f6\u200b\u64a4\u56de\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\u3002 \u200b\u60a8\u200b\u53ef\u4ee5\u200b\u901a\u8fc7\u200b\u70b9\u51fb\u200b\u672c\u9875\u200b\u9876\u90e8\u200b\u6216\u200b\u4e0b\u9762\u200b\u7684\u200b\u6309\u94ae\u200b\u968f\u65f6\u200b\u64a4\u56de\u200b\u60a8\u200b\u7684\u200b\u540c\u610f\u200b\uff1a

\u200b\u9690\u79c1\u200b\u63a7\u5236\u200b

\u200b\u7136\u800c\u200b\uff0c\u200b\u8bf7\u200b\u6ce8\u610f\u200b\uff0c\u200b\u8fd9\u200b\u4e0d\u4f1a\u200b\u5f71\u54cd\u200b\u64a4\u56de\u200b\u4e4b\u524d\u200b\u7684\u200b\u5904\u7406\u200b\u7684\u200b\u5408\u6cd5\u6027\u200b\uff0c\u200b\u4e5f\u200b\u4e0d\u4f1a\u200b\u5f71\u54cd\u200b\u5f53\u200b\u9002\u7528\u6cd5\u5f8b\u200b\u5141\u8bb8\u200b\u65f6\u200b\uff0c\u200b\u57fa\u4e8e\u200b\u9664\u200b\u540c\u610f\u200b\u4e4b\u5916\u200b\u7684\u200b\u5408\u6cd5\u200b\u5904\u7406\u200b\u7406\u7531\u200b\u8fdb\u884c\u200b\u7684\u200b\u60a8\u200b\u7684\u200b\u4e2a\u4eba\u4fe1\u606f\u200b\u7684\u200b\u5904\u7406\u200b\u3002

"},{"location":"zh/about/privacy/#cookies","title":"Cookies\u200b\u548c\u200b\u7c7b\u4f3c\u200b\u6280\u672f","text":"

\u200b\u5927\u591a\u6570\u200b\u7f51\u7edc\u200b\u6d4f\u89c8\u5668\u200b\u9ed8\u8ba4\u8bbe\u7f6e\u200b\u4e3a\u200b\u63a5\u53d7\u200bcookies\u3002 \u200b\u5982\u679c\u200b\u60a8\u200b\u613f\u610f\u200b\uff0c\u200b\u60a8\u200b\u901a\u5e38\u200b\u53ef\u4ee5\u200b\u9009\u62e9\u200b\u8bbe\u7f6e\u200b\u60a8\u200b\u7684\u200b\u6d4f\u89c8\u5668\u200b\u4ee5\u200b\u5220\u9664\u200b\u6216\u200b\u62d2\u7edd\u200b\u6d4f\u89c8\u5668\u200bcookies\u3002 \u200b\u8bf7\u200b\u6ce8\u610f\u200b\uff0c\u200b\u5982\u679c\u200b\u60a8\u200b\u9009\u62e9\u200b\u5220\u9664\u200b\u6216\u200b\u62d2\u7edd\u200bcookies\uff0c\u200b\u8fd9\u200b\u5c06\u200b\u4e0d\u4f1a\u200b\u5f71\u54cd\u200b\u6211\u4eec\u200b\u670d\u52a1\u200b\u7684\u200b\u53ef\u7528\u6027\u200b\u548c\u200b\u529f\u80fd\u200b\u3002

"},{"location":"zh/about/privacy/#9","title":"9. \u200b\u4e0d\u200b\u8ffd\u8e2a\u200b\u529f\u80fd\u200b\u7684\u200b\u63a7\u5236","text":"

\u200b\u5927\u591a\u6570\u200b\u7f51\u7edc\u200b\u6d4f\u89c8\u5668\u200b\u548c\u200b\u4e00\u4e9b\u200b\u79fb\u52a8\u200b\u64cd\u4f5c\u7cfb\u7edf\u200b\u548c\u200b\u79fb\u52a8\u200b\u5e94\u7528\u7a0b\u5e8f\u200b\u5305\u62ec\u200b\u4e00\u4e2a\u200b\u60a8\u200b\u53ef\u4ee5\u200b\u6fc0\u6d3b\u200b\u7684\u200b\u4e0d\u200b\u8ffd\u8e2a\u200b\uff08\u201cDNT\u201d\uff09\u200b\u529f\u80fd\u200b\u6216\u200b\u8bbe\u7f6e\u200b\uff0c\u200b\u4ee5\u200b\u8868\u8fbe\u200b\u60a8\u200b\u7684\u200b\u9690\u79c1\u200b\u504f\u597d\u200b\uff0c\u200b\u4e0d\u200b\u5e0c\u671b\u200b\u6709\u5173\u200b\u60a8\u200b\u7684\u200b\u5728\u7ebf\u200b\u6d4f\u89c8\u200b\u6d3b\u52a8\u200b\u7684\u200b\u6570\u636e\u200b\u88ab\u200b\u76d1\u63a7\u200b\u548c\u200b\u6536\u96c6\u200b\u3002 \u200b\u5230\u200b\u76ee\u524d\u4e3a\u6b62\u200b\uff0c\u200b\u8fd8\u200b\u6ca1\u6709\u200b\u4e3a\u200b\u8bc6\u522b\u200b\u548c\u200b\u5b9e\u65bd\u200bDNT\u200b\u4fe1\u53f7\u200b\u5236\u5b9a\u200b\u7edf\u4e00\u200b\u7684\u200b\u6280\u672f\u6807\u51c6\u200b\u3002 \u200b\u867d\u7136\u200b\u6211\u4eec\u200b\u4e0d\u80fd\u200b\u627f\u8bfa\u200b\u5c0a\u91cd\u200b\u6bcf\u200b\u4e00\u4e2a\u200bDNT\u200b\u4fe1\u53f7\u200b\uff0c\u200b\u6211\u4eec\u200b\u529b\u6c42\u200b\u5c0a\u91cd\u200b\u6240\u6709\u200b\u5728\u6280\u672f\u4e0a\u200b\u53ef\u884c\u200b\u7684\u200b\u6b64\u7c7b\u200b\u8bf7\u6c42\u200b\u3002

\u200b\u52a0\u5229\u798f\u5c3c\u4e9a\u200b\u6cd5\u5f8b\u200b\u8981\u6c42\u200b\u6211\u4eec\u200b\u544a\u8bc9\u60a8\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u54cd\u5e94\u200b\u7f51\u7edc\u200b\u6d4f\u89c8\u5668\u200b\u7684\u200bDNT\u200b\u4fe1\u53f7\u200b\u3002 \u200b\u7531\u4e8e\u200b\u6211\u4eec\u200b\u4e0d\u80fd\u200b\u4fdd\u8bc1\u200b\u8bc6\u522b\u200b\u548c\u200b\u5c0a\u91cd\u200b\u6240\u6709\u200bDNT\u200b\u4fe1\u53f7\u200b\uff0c\u200b\u6211\u4eec\u200b\u76ee\u524d\u200b\u4e0d\u200b\u5bf9\u200b\u5b83\u4eec\u200b\u505a\u51fa\u200b\u54cd\u5e94\u200b\u3002

"},{"location":"zh/about/privacy/#10","title":"10. \u200b\u67d0\u4e9b\u200b\u7ba1\u8f96\u533a\u200b\u7684\u200b\u5c45\u6c11\u200b\u662f\u5426\u200b\u6709\u200b\u7279\u5b9a\u200b\u7684\u200b\u9690\u79c1\u200b\u6743\u5229\u200b\uff1f","text":"

\u200b\u5426\u200b\u3002

\u200b\u6240\u6709\u200b\u7537\u5973\u200b\u751f\u800c\u5e73\u7b49\u200b\u3002

\u200b\u6211\u4eec\u200b\u5411\u200b\u6240\u6709\u200b\u4e2a\u4eba\u200b\u63d0\u4f9b\u200b\u76f8\u540c\u200b\u7684\u200b\u9690\u79c1\u200b\u6743\u5229\u200b\uff0c\u200b\u65e0\u8bba\u200b\u4ed6\u4eec\u200b\u7684\u200b\u4f4d\u7f6e\u200b\u5982\u4f55\u200b\u3002

\u200b\u8bf7\u200b\u653e\u5fc3\u200b\uff0c\u200b\u6211\u4eec\u200b\u5c06\u200b\u4ee5\u200b\u6211\u4eec\u200b\u5e0c\u671b\u200b\u88ab\u200b\u5bf9\u5f85\u200b\u7684\u200b\u76f8\u540c\u200b\u7684\u200b\u5c0a\u91cd\u200b\u548c\u200b\u5c0a\u4e25\u200b\u5bf9\u5f85\u200b\u60a8\u200b\u3002

"},{"location":"zh/about/privacy/#11","title":"11. \u200b\u60a8\u200b\u5982\u4f55\u200b\u67e5\u770b\u200b\u3001\u200b\u66f4\u65b0\u200b\u6216\u200b\u5220\u9664\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u7684\u200b\u6570\u636e\u200b\uff1f","text":"

\u200b\u60a8\u200b\u51e0\u4e4e\u200b\u4e0d\u200b\u53ef\u80fd\u200b\u67e5\u770b\u200b\u3001\u200b\u66f4\u65b0\u200b\u6216\u200b\u5220\u9664\u200b\u6211\u4eec\u200b\u6536\u96c6\u200b\u7684\u200b\u6570\u636e\u200b\uff0c\u200b\u56e0\u4e3a\u200b\u6211\u4eec\u200b\u4e0d\u200b\u4ece\u200b\u60a8\u200b\u90a3\u91cc\u200b\u6536\u96c6\u200b\u4efb\u4f55\u200b\u53ef\u200b\u8bc6\u522b\u200b\u7684\u200b\u4e2a\u4eba\u200b\u6570\u636e\u200b\uff0c\u200b\u4e5f\u200b\u65e0\u6cd5\u200b\u786e\u5b9a\u200b\u54ea\u4e9b\u200b\u6570\u636e\u200b\u662f\u200b\u5c5e\u4e8e\u200b\u60a8\u200b\u7684\u200b\u3002

"},{"location":"zh/about/privacy/#12","title":"12. \u200b\u6211\u4eec\u200b\u662f\u5426\u200b\u4f1a\u200b\u66f4\u65b0\u200b\u6b64\u200b\u58f0\u660e\u200b\uff1f","text":"

\u200b\u7b80\u800c\u8a00\u4e4b\u200b

\u200b\u662f\u200b\u7684\u200b\uff0c\u200b\u6211\u4eec\u200b\u5c06\u200b\u6839\u636e\u200b\u9700\u8981\u200b\u66f4\u65b0\u200b\u6b64\u200b\u58f0\u660e\u200b\u4ee5\u200b\u4fdd\u6301\u200b\u4e0e\u200b\u76f8\u5173\u200b\u6cd5\u5f8b\u200b\u7684\u200b\u4e00\u81f4\u200b\u3002

\u200b\u6211\u4eec\u200b\u53ef\u80fd\u200b\u4f1a\u200b\u4e0d\u65f6\u200b\u66f4\u65b0\u200b\u6b64\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u3002 \u200b\u66f4\u65b0\u200b\u540e\u200b\u7684\u200b\u7248\u672c\u200b\u5c06\u200b\u901a\u8fc7\u200b\u66f4\u65b0\u200b\u9876\u90e8\u200b\u7684\u200b\u201c\u200b\u6700\u540e\u200b\u4fee\u8ba2\u200b\u65e5\u671f\u200b\u201d\u200b\u6765\u200b\u8868\u793a\u200b\u3002 \u200b\u5982\u679c\u200b\u6211\u4eec\u200b\u8fdb\u884c\u200b\u4efb\u4f55\u200b\u91cd\u5927\u200b\u66f4\u6539\u200b\uff0c\u200b\u6211\u4eec\u200b\u5c06\u200b\u901a\u8fc7\u200b\u5728\u200b\u672c\u9875\u200b\u53d1\u5e03\u200b\u65b0\u200b\u7684\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\u6765\u200b\u901a\u77e5\u200b\u60a8\u200b\u3002 \u200b\u7531\u4e8e\u200b\u6211\u4eec\u200b\u4e0d\u200b\u6536\u96c6\u200b\u60a8\u200b\u7684\u200b\u4efb\u4f55\u200b\u8054\u7cfb\u200b\u4fe1\u606f\u200b\uff0c\u200b\u6211\u4eec\u200b\u65e0\u6cd5\u200b\u76f4\u63a5\u200b\u901a\u77e5\u200b\u60a8\u200b\u3002 \u200b\u6211\u4eec\u200b\u9f13\u52b1\u200b\u60a8\u200b\u7ecf\u5e38\u200b\u67e5\u770b\u200b\u672c\u200b\u9690\u79c1\u200b\u58f0\u660e\u200b\uff0c\u200b\u4ee5\u200b\u4e86\u89e3\u200b\u6211\u4eec\u200b\u5982\u4f55\u200b\u4fdd\u62a4\u200b\u60a8\u200b\u7684\u200b\u4fe1\u606f\u200b\u3002

"}]} \ No newline at end of file diff --git a/zh/flat_dict/index.html b/zh/flat_dict/index.html index b843fd05..ec01531c 100644 --- a/zh/flat_dict/index.html +++ b/zh/flat_dict/index.html @@ -2908,7 +2908,8 @@

FlatDict1529 1530 1531 -1532

class FlatDict(dict, metaclass=Dict):
+1532
+1533
class FlatDict(dict, metaclass=Dict):
     r"""
     `FlatDict` with attribute-style access.
 
@@ -3189,1139 +3190,1140 @@ 

FlatDict try: if name in self.__dict__: return self.__dict__[name] - if name in self.__class__.__dict__: - return self.__class__.__dict__[name] - return super().getattr(name, default) # type: ignore[misc] - except AttributeError: - if default is not Null: - return default - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") from None - - def setattr(self, name: str, value: Any) -> None: - r""" - Set attribute of `FlatDict`. - - Note that it won't alter values in `FlatDict`. - - Args: - name: - value: - - Warns: - RuntimeWarning: If name already exists in `FlatDict`. - - Examples: - >>> d = FlatDict() - >>> d.setattr('attr', 'value') - >>> d.getattr('attr') - 'value' - >>> d.set('d', 1013) - >>> d.setattr('d', 1031) # RuntimeWarning: d already exists in FlatDict. - >>> d.get('d') - 1013 - >>> d.d - 1013 - >>> d.getattr('d') - 1031 - """ - - if name in self: - warn( - f"{name} already exists in {self.__class__.__name__}.\n" - f"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.", - RuntimeWarning, - ) - self.__dict__[name] = value - - def delattr(self, name: str) -> None: - r""" - Delete attribute of `FlatDict`. - - Note that it won't delete values in `FlatDict`. - - Args: - name: - - Examples: - >>> d = FlatDict() - >>> d.setattr('name', 'chang') - >>> d.getattr('name') - 'chang' - >>> d.delattr('name') - >>> d.getattr('name') - Traceback (most recent call last): - AttributeError: 'FlatDict' object has no attribute 'name' - """ - - del self.__dict__[name] - - def hasattr(self, name: str) -> bool: - r""" - Determine if an attribute exists in `FlatDict`. - - Args: - name: - - Returns: - (bool): - - Examples: - >>> d = FlatDict() - >>> d.setattr('name', 'chang') - >>> d.hasattr('name') - True - >>> d.delattr('name') - >>> d.hasattr('name') - False - """ - - try: - if name in self.__dict__ or name in self.__class__.__dict__: - return True - return super().hasattr(name) # type: ignore[misc] - except AttributeError: - return False - - def dict(self, flatten: bool = False) -> Mapping | Sequence | Set: - r""" - Convert `FlatDict` to other `Mapping`. - - Args: - flatten: Whether to flatten [`NestedDict`][chanfig.NestedDict]. - - Returns: - (Mapping): - - See Also: - [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`. - - **Alias**: - - + `to_dict` - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.dict() - {'a': 1, 'b': 2, 'c': 3} - """ - - return to_dict(self, flatten) - - def to_dict(self, flatten: bool = False) -> Mapping | Sequence | Set: - r""" - Alias of [`dict`][chanfig.FlatDict.dict]. - """ - - return self.dict(flatten) - - @classmethod - def from_dict(cls, obj: Mapping | Sequence) -> Any: # pylint: disable=R0911 - r""" - Convert `Mapping` or `Sequence` to `FlatDict`. - - Examples: - >>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3}) - FlatDict( - ('a'): 1 - ('b'): 2 - ('c'): 3 - ) - >>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)]) - FlatDict( - ('a'): 1 - ('b'): 2 - ('c'): 3 - ) - >>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}]) - [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)] - >>> FlatDict.from_dict({1, 2, 3}) - Traceback (most recent call last): - TypeError: Expected Mapping or Sequence, but got <class 'set'>. - """ - - if obj is None: - return cls() - if issubclass(cls, FlatDict): - cls = cls.empty # type: ignore[assignment] # pylint: disable=W0642 - if isinstance(obj, Mapping): - return cls(obj) - if isinstance(obj, Sequence): - try: - return cls(obj) - except ValueError: - return [cls(json) for json in obj] - raise TypeError(f"Expected Mapping or Sequence, but got {type(obj)}.") - - def sort(self, key: Callable | None = None, reverse: bool = False) -> Self: - r""" - Sort `FlatDict`. - - Returns: - (FlatDict): - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.sort().dict() - {'a': 1, 'b': 2, 'c': 3} - >>> d = FlatDict(b=2, c=3, a=1) - >>> d.sort().dict() - {'a': 1, 'b': 2, 'c': 3} - >>> a = [1] - >>> d = FlatDict(z=0, a=a) - >>> a.append(2) - >>> d.sort().dict() - {'a': [1, 2], 'z': 0} - """ - - items = sorted(self.items(), key=key, reverse=reverse) - self.clear() - for k, v in items: # pylint: disable=C0103 - self[k] = v - return self - - def interpolate( # pylint: disable=R0912 - self, use_variable: bool = True, interpolators: MutableMapping | None = None, unsafe_eval: bool = False - ) -> Self: - r""" - Perform Variable interpolation. - - Variable interpolation allows you to set the value of one key to be the value of another key easily. - - Args: - use_variable: Whether to convert values to `Variable` objects. - interpolators: Mapping contains values for interpolation. Defaults to `self`. - unsafe_eval: Whether to evaluate interpolated values. - - Raises: - 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: - >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}") - >>> d.dict() - {'a': 1, 'b': '${a}', 'c': '${a}.${b}'} - >>> d.interpolate(unsafe_eval=True).dict() - {'a': 1, 'b': 1, 'c': 1.1} - >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}") - >>> d.dict() - {'a': 1, 'b': '${a}', 'c': '${a}.${b}'} - >>> d.interpolate().dict() - {'a': 1, 'b': 1, 'c': '1.1'} - >>> isinstance(d.a, Variable) - True - >>> d.a += 1 - >>> d.dict() - {'a': 2, 'b': 2, 'c': '1.1'} - >>> d.a is d.b - True - >>> d.b is d.c - False - >>> d = FlatDict(a=1, b="${a}", c="${b}") - >>> d.dict() - {'a': 1, 'b': '${a}', 'c': '${b}'} - >>> d.interpolate(False).dict() - {'a': 1, 'b': 1, 'c': 1} - >>> isinstance(d.a, Variable) - False - >>> d.a += 1 - >>> d.dict() - {'a': 2, 'b': 1, 'c': 1} - >>> d = FlatDict(a=1, b="${b}", c="${b}") - >>> d.interpolate().dict() - Traceback (most recent call last): - ValueError: Cannot interpolate b to itself. - >>> d = FlatDict(a="${b}", b="${c}", c="${d}", d="${a}") - >>> d.interpolate().dict() - Traceback (most recent call last): - ValueError: Circular reference found: a->b->c->d->a. - >>> d = FlatDict(a=1, b="${a}", c="${d}") - >>> d.interpolate().dict() - Traceback (most recent call last): - ValueError: d is not found in FlatDict( - ('a'): '1' - ('b'): '${a}' - ('c'): '${d}' - ). - """ - # pylint: disable=C0103 - - interpolators = interpolators or self - placeholders: dict[str, list[str]] = {} - for key, value in self.all_items(): - if isinstance(value, list): - for v in value: - self.find_placeholders(key, v, placeholders) - elif isinstance(value, Mapping): - for v in value.values(): - self.find_placeholders(key, v, placeholders) - else: - self.find_placeholders(key, value, placeholders) - circular_references = find_circular_reference(placeholders) - if circular_references: - raise ValueError(f"Circular reference found: {'->'.join(circular_references)}.") - if use_variable: - placeholder_names = {i for j in placeholders.values() for i in j} - for name in list(placeholder_names.difference(placeholders.keys())): - if name not in interpolators: - raise ValueError(f"{name} is not found in {interpolators}.") - if not isinstance(interpolators[name], Variable): - interpolators[name] = Variable(interpolators[name]) - for key, value in placeholders.items(): - if isinstance(self[key], list): - for index, v in enumerate(self[key]): - self[key][index] = self.substitute(v, interpolators, value) - elif isinstance(self[key], Mapping): - for k, v in self[key].items(): - self[key][k] = self.substitute(v, interpolators, value) - else: - self[key] = self.substitute(self[key], interpolators, value) - if unsafe_eval and isinstance(self[key], str): - with suppress(SyntaxError): - self[key] = eval(self[key]) # pylint: disable=W0123 - return self - - @staticmethod - def find_placeholders(key, value, placeholders): - placeholder = find_placeholders(value) - if placeholder: - for index, name in enumerate(placeholder): - if name.startswith("."): - placeholder[index] = key.rsplit(".", 1)[0] + name - if key == name: - raise ValueError(f"Cannot interpolate {key} to itself.") - placeholders[key] = placeholder - - @staticmethod - def substitute(placeholder, interpolators, value): - try: - if len(value) == 1 and placeholder.startswith("${") and placeholder.endswith("}"): - return interpolators[value[0]] - return placeholder.replace("$", "").format(**interpolators) - except KeyError as exc: - raise ValueError(f"{exc} is not found in {interpolators}.") from None - - def merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> Self: - r""" - Merge `other` into `FlatDict`. - - Args: - *args: `Mapping` or `Sequence` to be merged. - overwrite: Whether to overwrite existing values. - **kwargs: `Mapping` to be merged. - - Returns: - self: - - **Alias**: - - + `union` - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> n = {'b': 'b', 'c': 'c', 'd': 'd'} - >>> d.merge(n).dict() - {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'} - >>> l = [('c', 3), ('d', 4)] - >>> d.merge(l).dict() - {'a': 1, 'b': 'b', 'c': 3, 'd': 4} - >>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict() # alias - {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'} - >>> d = FlatDict() - >>> d.merge({1: 1, 2: 2, 3:3}).dict() - {1: 1, 2: 2, 3: 3} - >>> d.merge(d.clone()).dict() - {1: 1, 2: 2, 3: 3} - >>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict() - {1: 1, 2: 2, 3: 3, 4: 4, 5: 5} - """ - - if len(args) == 1: - args = args[0] - if isinstance(args, (PathLike, str, bytes)): - args = self.load(args) # type: ignore[assignment] - warn( - "merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.", - PendingDeprecationWarning, - ) - self._merge(self, args, overwrite=overwrite) - elif len(args) > 1: - self._merge(self, args, overwrite=overwrite) - if kwargs: - self._merge(self, kwargs, overwrite=overwrite) - return self - - @staticmethod - def _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping: - if not that: - return this - if isinstance(that, Mapping): - that = that.items() - for key, value in that: - if key in this and isinstance(this[key], Mapping): - if isinstance(value, Mapping): - FlatDict._merge(this[key], value) - elif overwrite: - if isinstance(value, FlatDict): - this.set(key, value) - else: - this[key] = value - elif overwrite or key not in this: - this.set(key, value) - return this - - def union(self, *args: Any, **kwargs: Any) -> Self: - r""" - Alias of [`merge`][chanfig.FlatDict.merge]. - """ - return self.merge(*args, **kwargs) - - def merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> Self: - r""" - Merge content of `file` into `FlatDict`. - - Args: - file (File): - *args: Passed to [`load`][chanfig.FlatDict.load]. - **kwargs: Passed to [`load`][chanfig.FlatDict.load]. - - Returns: - self: - - Examples: - >>> d = FlatDict(a=1, b=1) - >>> d.merge_from_file("tests/test.yaml").dict() - {'a': 1, 'b': 2, 'c': 3} - """ - - return self.merge(self.load(file, *args, **kwargs)) - - def intersect(self, other: Mapping | Iterable | PathStr) -> Self: - r""" - Intersection of `FlatDict` and `other`. - - Args: - other (Mapping | Iterable | PathStr): - - Returns: - (FlatDict): - - **Alias**: - - + `inter` - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> n = {'b': 'b', 'c': 'c', 'd': 'd'} - >>> d.intersect(n).dict() - {} - >>> l = [('c', 3), ('d', 4)] - >>> d.intersect(l).dict() - {'c': 3} - >>> d.merge(l).intersect("tests/test.yaml").dict() - {'a': 1, 'b': 2, 'c': 3} - >>> d.intersect(1) - Traceback (most recent call last): - TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>. - >>> d.inter(FlatDict(b='b', c='c', d='d')).dict() # alias - {} - """ - - if isinstance(other, (PathLike, str, bytes)): - other = self.load(other) - if isinstance(other, (Mapping,)): - other = self.empty(other).items() - if not isinstance(other, Iterable): - raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.") - return self.empty(**{key: value for key, value in other if key in self and self[key] == value}) # type: ignore - - def inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self: - r""" - Alias of [`intersect`][chanfig.FlatDict.intersect]. - """ - return self.intersect(other, *args, **kwargs) - - def difference(self, other: Mapping | Iterable | PathStr) -> Self: - r""" - Difference between `FlatDict` and `other`. - - Args: - other: - - Returns: - (FlatDict): - - **Alias**: - - + `diff` - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> n = {'b': 'b', 'c': 'c', 'd': 'd'} - >>> d.difference(n).dict() - {'b': 'b', 'c': 'c', 'd': 'd'} - >>> l = [('c', 3), ('d', 4)] - >>> d.difference(l).dict() - {'d': 4} - >>> d.merge(l).difference("tests/test.yaml").dict() - {} - >>> d.difference(1) - Traceback (most recent call last): - TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>. - >>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict() # alias - {'b': 'b', 'c': 'c', 'd': 'd'} - """ - - if isinstance(other, (PathLike, str, bytes)): - other = self.load(other) - if isinstance(other, (Mapping,)): - other = self.empty(other).items() - if not isinstance(other, Iterable): - raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.") - return self.empty( - **{key: value for key, value in other if key not in self or self[key] != value} # type: ignore[misc] - ) - - def diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self: - r""" - Alias of [`difference`][chanfig.FlatDict.difference]. - """ - return self.difference(other, *args, **kwargs) - - def to(self, cls: str | TorchDevice | TorchDType) -> Self: # pragma: no cover - r""" - Convert values of `FlatDict` to target `cls`. - - Args: - cls (str | torch.device | torch.dtype): - - Returns: - self: - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.to(int) - Traceback (most recent call last): - TypeError: to() only support torch.dtype and torch.device, but got <class 'int'>. - """ - - # pylint: disable=C0103 - - if isinstance(cls, (str, TorchDevice, TorchDType)): - for k, v in self.all_items(): - if hasattr(v, "to"): - self[k] = v.to(cls) - return self - - raise TypeError(f"to() only support torch.dtype and torch.device, but got {cls}.") - - def cpu(self) -> Self: # pragma: no cover - r""" - Move all tensors to cpu. - - Returns: - self: - - Examples: - >>> import torch - >>> d = FlatDict(a=torch.tensor(1)) - >>> d.cpu().dict() # doctest: +SKIP - {'a': tensor(1, device='cpu')} - """ - - return self.to(TorchDevice("cpu")) - - def gpu(self) -> Self: # pragma: no cover - r""" - Move all tensors to gpu. - - Returns: - self: - - **Alias**: - - + `cuda` - - Examples: - >>> import torch - >>> d = FlatDict(a=torch.tensor(1)) - >>> d.gpu().dict() # doctest: +SKIP - {'a': tensor(1, device='cuda:0')} - >>> d.cuda().dict() # alias # doctest: +SKIP - {'a': tensor(1, device='cuda:0')} - """ - - return self.to(TorchDevice("cuda")) - - def cuda(self) -> Self: # pragma: no cover - r""" - Alias of [`gpu`][chanfig.FlatDict.gpu]. - """ - return self.gpu() - - def tpu(self) -> Self: # pragma: no cover - r""" - Move all tensors to tpu. - - Returns: - self: - - **Alias**: - - + `xla` - - Examples: - >>> import torch - >>> d = FlatDict(a=torch.tensor(1)) - >>> d.tpu().dict() # doctest: +SKIP - {'a': tensor(1, device='xla:0')} - >>> d.xla().dict() # alias # doctest: +SKIP - {'a': tensor(1, device='xla:0')} - """ - - return self.to(TorchDevice("xla")) - - def xla(self) -> Self: # pragma: no cover - r""" - Alias of [`tpu`][chanfig.FlatDict.tpu]. - """ - return self.tpu() - - def copy(self) -> Self: - r""" - Create a shallow copy of `FlatDict`. - - Returns: - (FlatDict): - - Examples: - >>> d = FlatDict(a=[]) - >>> d.setattr("name", "Chang") - >>> c = d.copy() - >>> c.dict() - {'a': []} - >>> d.a.append(1) - >>> c.dict() - {'a': [1]} - >>> c.getattr("name") - 'Chang' - """ - - return copy(self) - - def __deepcopy__(self, memo: Mapping | None = None) -> Self: - # pylint: disable=C0103 - - if memo is not None and id(self) in memo: - return memo[id(self)] - ret = self.empty() - ret.__dict__.update(deepcopy(self.__dict__)) - for k, v in self.items(): - if isinstance(v, FlatDict): - ret[k] = v.deepcopy(memo=memo) - else: - ret[k] = deepcopy(v) - return ret - - def deepcopy(self, memo: Mapping | None = None) -> Self: # pylint: disable=W0613 - r""" - Create a deep copy of `FlatDict`. - - Returns: - (FlatDict): - - **Alias**: - - + `clone` - - Examples: - >>> d = FlatDict(a=[]) - >>> d.setattr("name", "Chang") - >>> c = d.deepcopy() - >>> c.dict() - {'a': []} - >>> d.a.append(1) - >>> c.dict() - {'a': []} - >>> c.getattr("name") - 'Chang' - >>> d == d.clone() # alias - True - """ - - return deepcopy(self) - - def clone(self, memo: Mapping | None = None) -> Self: - r""" - Alias of [`deepcopy`][chanfig.FlatDict.deepcopy]. - """ - return self.deepcopy(memo=memo) - - def save( # pylint: disable=W1113 - self, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment] - ) -> None: - r""" - Save `FlatDict` to file. - - Raises: - ValueError: If save to `IO` and `method` is not specified. - TypeError: If save to unsupported extension. - - **Alias**: - - + `save` - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.save("tests/test.yaml") - >>> d.save("test.conf") - Traceback (most recent call last): - TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf. - >>> with open("test.yaml", "w") as f: - ... d.save(f) - Traceback (most recent call last): - ValueError: `method` must be specified when saving to IO. - """ - - if method is None: - if isinstance(file, (IOBase, IO)): - raise ValueError("`method` must be specified when saving to IO.") - method = splitext(file)[-1][1:] - extension = method.lower() - if extension in YAML: - return self.yaml(file=file, *args, **kwargs) # type: ignore[misc] # noqa: B026 - if extension in JSON: - return self.json(file=file, *args, **kwargs) # type: ignore[misc] # noqa: B026 - raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.") - - def dump( # pylint: disable=W1113 - self, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment] - ) -> None: - r""" - Alias of [`save`][chanfig.FlatDict.save]. - """ - return self.save(file, method, *args, **kwargs) - - @classmethod - def load( # pylint: disable=W1113 - cls, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment] - ) -> Self: - """ - Load `FlatDict` from file. - - Args: - file: File to load from. - method: File type, should be in `JSON` or `YAML`. - - Returns: - (FlatDict): - - Raises: - ValueError: If load from `IO` and `method` is not specified. - TypeError: If dump to unsupported extension. - - Examples: - >>> d = FlatDict.load("tests/test.yaml") - >>> d.dict() - {'a': 1, 'b': 2, 'c': 3} - >>> d.load("tests/test.conf") - Traceback (most recent call last): - TypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf. - >>> with open("tests/test.yaml") as f: - ... d.load(f) - Traceback (most recent call last): - ValueError: `method` must be specified when loading from IO. - """ - - if method is None: - if isinstance(file, (IOBase, IO)): - raise ValueError("`method` must be specified when loading from IO.") - method = splitext(file)[-1][1:] - extension = method.lower() - if extension in JSON: - return cls.from_json(file, *args, **kwargs) - if extension in YAML: - return cls.from_yaml(file, *args, **kwargs) - raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.") - - def json(self, file: File, *args: Any, **kwargs: Any) -> None: - r""" - 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: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.json("tests/test.json") - """ - - with self.open(file, mode="w") as fp: # pylint: disable=C0103 - fp.write(self.jsons(*args, **kwargs)) - - @classmethod - def from_json(cls, file: File, *args: Any, **kwargs: Any) -> Self: - r""" - 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: - (FlatDict): - - Examples: - >>> d = FlatDict.from_json('tests/test.json') - >>> d.dict() - {'a': 1, 'b': 2, 'c': 3} - """ - - with cls.open(file) as fp: # pylint: disable=C0103 - if isinstance(file, (IOBase, IO)): - return cls.from_jsons(fp.getvalue(), *args, **kwargs) # type: ignore[union-attr] - return cls.from_jsons(fp.read(), *args, **kwargs) - - def jsons(self, *args: Any, **kwargs: Any) -> str: - r""" - Dump `FlatDict` to json string. - - Returns: - (str): - - Examples: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.jsons() - '{\n "a": 1,\n "b": 2,\n "c": 3\n}' - """ - - kwargs.setdefault("cls", JsonEncoder) - kwargs.setdefault("indent", self.getattr("indent", 2)) - return json_dumps(self.dict(), *args, **kwargs) - - @classmethod - def from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> Self: - r""" - Construct `FlatDict` from json string. - - Returns: - (FlatDict): - - Examples: - >>> FlatDict.from_jsons('{\n "a": 1,\n "b": 2,\n "c": 3\n}').dict() - {'a': 1, 'b': 2, 'c': 3} - >>> FlatDict.from_jsons('[["a", 1], ["b", 2], ["c", 3]]').dict() - {'a': 1, 'b': 2, 'c': 3} - >>> FlatDict.from_jsons('[{"a": 1}, {"b": 2}, {"c": 3}]') - [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)] - """ - - return cls.from_dict(json_loads(string, *args, **kwargs)) - - def yaml(self, file: File, *args: Any, **kwargs: Any) -> None: - r""" - 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: - >>> d = FlatDict(a=1, b=2, c=3) - >>> d.yaml("tests/test.yaml") - """ - - with self.open(file, mode="w") as fp: # pylint: disable=C0103 - self.yamls(fp, *args, **kwargs) - - @classmethod - def from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> Self: - r""" - 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: - (FlatDict): - - Examples: - >>> FlatDict.from_yaml('tests/test.yaml').dict() - {'a': 1, 'b': 2, 'c': 3} - """ - - kwargs.setdefault("Loader", YamlLoader) - with cls.open(file) as fp: # pylint: disable=C0103 - if isinstance(file, (IOBase, IO)): - return cls.from_yamls(fp.getvalue(), *args, **kwargs) # type: ignore[union-attr] - return cls.from_dict(yaml_load(fp, *args, **kwargs)) - - def yamls(self, *args: Any, **kwargs: Any) -> str: - r""" - Dump `FlatDict` to yaml string. - - Returns: - (str): - - Examples: - >>> FlatDict(a=1, b=2, c=3).yamls() - 'a: 1\nb: 2\nc: 3\n' - """ - - kwargs.setdefault("Dumper", YamlDumper) - kwargs.setdefault("indent", self.getattr("indent", 2)) - return yaml_dump(self.dict(), *args, **kwargs) - - @classmethod - def from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> Self: - r""" - Construct `FlatDict` from yaml string. - - Returns: - (FlatDict): - - Examples: - >>> FlatDict.from_yamls('a: 1\nb: 2\nc: 3\n').dict() - {'a': 1, 'b': 2, 'c': 3} - >>> FlatDict.from_yamls('- - a\n - 1\n- - b\n - 2\n- - c\n - 3\n').dict() - {'a': 1, 'b': 2, 'c': 3} - >>> FlatDict.from_yamls('- a: 1\n- b: 2\n- c: 3\n') - [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)] - """ - - kwargs.setdefault("Loader", SafeLoader) - return cls.from_dict(yaml_load(string, *args, **kwargs)) - - @staticmethod - @contextmanager - def open(file: File, *args: Any, encoding: str = "utf-8", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]: - r""" - 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. - - Args: - file: File path or IO. - *args: Additional arguments passed to `open`. - Defaults to (). - **kwargs: Any - Additional keyword arguments passed to `open`. - Defaults to {}. - - Yields: - (Generator[IOBase | IO, Any, Any]): - - Examples: - >>> with FlatDict.open("tests/test.yaml") as fp: - ... print(fp.read()) - a: 1 - b: 2 - c: 3 - <BLANKLINE> - >>> io = open("tests/test.yaml") - >>> with FlatDict.open(io) as fp: - ... print(fp.read()) - a: 1 - b: 2 - c: 3 - <BLANKLINE> - >>> with FlatDict.open(123, mode="w") as fp: - ... print(fp.read()) - Traceback (most recent call last): - TypeError: expected str, bytes, os.PathLike, IO or IOBase, not int - """ - - if isinstance(file, (IOBase, IO)): - yield file - elif isinstance(file, (PathLike, str, bytes)): - try: - file = open(file, *args, encoding=encoding, **kwargs) # type: ignore[call-overload] # noqa: SIM115 - yield file # type: ignore[misc] - finally: - with suppress(Exception): - file.close() # type: ignore[union-attr] - else: - raise TypeError(f"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}") - - @classmethod - def empty(cls, *args: Any, **kwargs: Any) -> Self: - r""" - 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: - (FlatDict): - - See Also: - [`empty_like`][chanfig.FlatDict.empty_like] - - Examples: - >>> d = FlatDict(a=[]) - >>> c = d.empty() - >>> c.dict() - {} - """ - - empty = cls.__new__(cls) - empty.merge(*args, **kwargs) # pylint: disable=W0212 - return empty - - def empty_like(self, *args: Any, **kwargs: Any) -> Self: - r""" - Initialise an empty copy of `FlatDict`. - - This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`. - - For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this - method. - - Returns: - (FlatDict): - - See Also: - [`empty`][chanfig.FlatDict.empty] - - Examples: - >>> d = FlatDict(a=[]) - >>> d.setattr("name", "Chang") - >>> c = d.empty_like() - >>> c.dict() - {} - >>> c.getattr("name") - 'Chang' - """ - - empty = self.empty(*args, **kwargs) - empty.__dict__.update(self.__dict__) - return empty - - def all_keys(self) -> Generator: - r""" - Equivalent to `keys`. - - This method is provided solely to make methods work on both `FlatDict` and `NestedDict`. - - See Also: - [`all_keys`][chanfig.NestedDict.all_keys] - """ - yield from self.keys() - - def all_values(self) -> Generator: - r""" - Equivalent to `keys`. - - This method is provided solely to make methods work on both `FlatDict` and `NestedDict`. - - See Also: - [`all_values`][chanfig.NestedDict.all_values] - """ - yield from self.values() - - def all_items(self) -> Generator: - r""" - Equivalent to `keys`. - - This method is provided solely to make methods work on both `FlatDict` and `NestedDict`. - - See Also: - [`all_items`][chanfig.NestedDict.all_items] - """ - yield from self.items() - - def dropnull(self) -> Self: - r""" - Drop key-value pairs with `Null` value. - - Returns: - (FlatDict): - - **Alias**: - - + `dropna` - - Examples: - >>> d = FlatDict(a=Null, b=Null, c=3) - >>> d.dict() - {'a': Null, 'b': Null, 'c': 3} - >>> d.dropnull().dict() - {'c': 3} - >>> d.dropna().dict() # alias - {'c': 3} - """ - - return self.empty({k: v for k, v in self.all_items() if v is not Null}) - - def dropna(self) -> Self: - r""" - Alias of [`dropnull`][chanfig.FlatDict.dropnull]. - """ - return self.dropnull() - - @staticmethod - def extra_repr() -> str: # pylint: disable=C0116 - return "" - - def __repr__(self) -> str: - extra_lines = [] - extra_repr = self.extra_repr() - # empty string will be split into list [''] - if extra_repr: - extra_lines = extra_repr.split("\n") - child_lines = [] - for key, value in self.items(): - key_repr = repr(key) - value_repr = repr(value) - value_repr = self._add_indent(value_repr) - child_lines.append(f"({key_repr}): {value_repr}") - # child_lines.append(f"{key_repr}: {value_repr}") - lines = extra_lines + child_lines - - main_repr = self.__class__.__name__ + "(" - if lines: - # simple one-liner info, which most builtin Modules will use - if len(extra_lines) == 1 and not child_lines: - main_repr += extra_lines[0] - elif len(child_lines) == 1 and not extra_lines and len(child_lines[0]) < 10: - main_repr += child_lines[0] - else: - main_repr += "\n " + "\n ".join(lines) + "\n" - - main_repr += ")" - return main_repr - - def _add_indent(self, text: str) -> str: - lines = text.split("\n") - # don't do anything for single-line stuff - if len(lines) == 1: - return text - first = lines.pop(0) - lines = [(self.getattr("indent", 2) * " ") + line for line in lines] - text = "\n".join(lines) - text = first + "\n" + text - return text - - def __format__(self, format_spec: str) -> str: - return repr(self.empty({k: v.__format__(format_spec) for k, v in self.all_items()})) - - def __hash__(self): - return hash(frozenset(self.items())) - - def _ipython_display_(self): # pragma: no cover - return repr(self) - - def _ipython_canary_method_should_not_exist_(self): # pragma: no cover - return None - - def aihwerij235234ljsdnp34ksodfipwoe234234jlskjdf(self): # pragma: no cover - return None - - def __rich__(self): # pragma: no cover - return self.__repr__() + for cls in self.__class__.__mro__: + if name in cls.__dict__: + return cls.__dict__[name] + return super().getattr(name, default) # type: ignore[misc] + except AttributeError: + if default is not Null: + return default + raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") from None + + def setattr(self, name: str, value: Any) -> None: + r""" + Set attribute of `FlatDict`. + + Note that it won't alter values in `FlatDict`. + + Args: + name: + value: + + Warns: + RuntimeWarning: If name already exists in `FlatDict`. + + Examples: + >>> d = FlatDict() + >>> d.setattr('attr', 'value') + >>> d.getattr('attr') + 'value' + >>> d.set('d', 1013) + >>> d.setattr('d', 1031) # RuntimeWarning: d already exists in FlatDict. + >>> d.get('d') + 1013 + >>> d.d + 1013 + >>> d.getattr('d') + 1031 + """ + + if name in self: + warn( + f"{name} already exists in {self.__class__.__name__}.\n" + f"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.", + RuntimeWarning, + ) + self.__dict__[name] = value + + def delattr(self, name: str) -> None: + r""" + Delete attribute of `FlatDict`. + + Note that it won't delete values in `FlatDict`. + + Args: + name: + + Examples: + >>> d = FlatDict() + >>> d.setattr('name', 'chang') + >>> d.getattr('name') + 'chang' + >>> d.delattr('name') + >>> d.getattr('name') + Traceback (most recent call last): + AttributeError: 'FlatDict' object has no attribute 'name' + """ + + del self.__dict__[name] + + def hasattr(self, name: str) -> bool: + r""" + Determine if an attribute exists in `FlatDict`. + + Args: + name: + + Returns: + (bool): + + Examples: + >>> d = FlatDict() + >>> d.setattr('name', 'chang') + >>> d.hasattr('name') + True + >>> d.delattr('name') + >>> d.hasattr('name') + False + """ + + try: + if name in self.__dict__ or name in self.__class__.__dict__: + return True + return super().hasattr(name) # type: ignore[misc] + except AttributeError: + return False + + def dict(self, flatten: bool = False) -> Mapping | Sequence | Set: + r""" + Convert `FlatDict` to other `Mapping`. + + Args: + flatten: Whether to flatten [`NestedDict`][chanfig.NestedDict]. + + Returns: + (Mapping): + + See Also: + [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`. + + **Alias**: + + + `to_dict` + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.dict() + {'a': 1, 'b': 2, 'c': 3} + """ + + return to_dict(self, flatten) + + def to_dict(self, flatten: bool = False) -> Mapping | Sequence | Set: + r""" + Alias of [`dict`][chanfig.FlatDict.dict]. + """ + + return self.dict(flatten) + + @classmethod + def from_dict(cls, obj: Mapping | Sequence) -> Any: # pylint: disable=R0911 + r""" + Convert `Mapping` or `Sequence` to `FlatDict`. + + Examples: + >>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3}) + FlatDict( + ('a'): 1 + ('b'): 2 + ('c'): 3 + ) + >>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)]) + FlatDict( + ('a'): 1 + ('b'): 2 + ('c'): 3 + ) + >>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}]) + [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)] + >>> FlatDict.from_dict({1, 2, 3}) + Traceback (most recent call last): + TypeError: Expected Mapping or Sequence, but got <class 'set'>. + """ + + if obj is None: + return cls() + if issubclass(cls, FlatDict): + cls = cls.empty # type: ignore[assignment] # pylint: disable=W0642 + if isinstance(obj, Mapping): + return cls(obj) + if isinstance(obj, Sequence): + try: + return cls(obj) + except ValueError: + return [cls(json) for json in obj] + raise TypeError(f"Expected Mapping or Sequence, but got {type(obj)}.") + + def sort(self, key: Callable | None = None, reverse: bool = False) -> Self: + r""" + Sort `FlatDict`. + + Returns: + (FlatDict): + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.sort().dict() + {'a': 1, 'b': 2, 'c': 3} + >>> d = FlatDict(b=2, c=3, a=1) + >>> d.sort().dict() + {'a': 1, 'b': 2, 'c': 3} + >>> a = [1] + >>> d = FlatDict(z=0, a=a) + >>> a.append(2) + >>> d.sort().dict() + {'a': [1, 2], 'z': 0} + """ + + items = sorted(self.items(), key=key, reverse=reverse) + self.clear() + for k, v in items: # pylint: disable=C0103 + self[k] = v + return self + + def interpolate( # pylint: disable=R0912 + self, use_variable: bool = True, interpolators: MutableMapping | None = None, unsafe_eval: bool = False + ) -> Self: + r""" + Perform Variable interpolation. + + Variable interpolation allows you to set the value of one key to be the value of another key easily. + + Args: + use_variable: Whether to convert values to `Variable` objects. + interpolators: Mapping contains values for interpolation. Defaults to `self`. + unsafe_eval: Whether to evaluate interpolated values. + + Raises: + 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: + >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}") + >>> d.dict() + {'a': 1, 'b': '${a}', 'c': '${a}.${b}'} + >>> d.interpolate(unsafe_eval=True).dict() + {'a': 1, 'b': 1, 'c': 1.1} + >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}") + >>> d.dict() + {'a': 1, 'b': '${a}', 'c': '${a}.${b}'} + >>> d.interpolate().dict() + {'a': 1, 'b': 1, 'c': '1.1'} + >>> isinstance(d.a, Variable) + True + >>> d.a += 1 + >>> d.dict() + {'a': 2, 'b': 2, 'c': '1.1'} + >>> d.a is d.b + True + >>> d.b is d.c + False + >>> d = FlatDict(a=1, b="${a}", c="${b}") + >>> d.dict() + {'a': 1, 'b': '${a}', 'c': '${b}'} + >>> d.interpolate(False).dict() + {'a': 1, 'b': 1, 'c': 1} + >>> isinstance(d.a, Variable) + False + >>> d.a += 1 + >>> d.dict() + {'a': 2, 'b': 1, 'c': 1} + >>> d = FlatDict(a=1, b="${b}", c="${b}") + >>> d.interpolate().dict() + Traceback (most recent call last): + ValueError: Cannot interpolate b to itself. + >>> d = FlatDict(a="${b}", b="${c}", c="${d}", d="${a}") + >>> d.interpolate().dict() + Traceback (most recent call last): + ValueError: Circular reference found: a->b->c->d->a. + >>> d = FlatDict(a=1, b="${a}", c="${d}") + >>> d.interpolate().dict() + Traceback (most recent call last): + ValueError: d is not found in FlatDict( + ('a'): '1' + ('b'): '${a}' + ('c'): '${d}' + ). + """ + # pylint: disable=C0103 + + interpolators = interpolators or self + placeholders: dict[str, list[str]] = {} + for key, value in self.all_items(): + if isinstance(value, list): + for v in value: + self.find_placeholders(key, v, placeholders) + elif isinstance(value, Mapping): + for v in value.values(): + self.find_placeholders(key, v, placeholders) + else: + self.find_placeholders(key, value, placeholders) + circular_references = find_circular_reference(placeholders) + if circular_references: + raise ValueError(f"Circular reference found: {'->'.join(circular_references)}.") + if use_variable: + placeholder_names = {i for j in placeholders.values() for i in j} + for name in list(placeholder_names.difference(placeholders.keys())): + if name not in interpolators: + raise ValueError(f"{name} is not found in {interpolators}.") + if not isinstance(interpolators[name], Variable): + interpolators[name] = Variable(interpolators[name]) + for key, value in placeholders.items(): + if isinstance(self[key], list): + for index, v in enumerate(self[key]): + self[key][index] = self.substitute(v, interpolators, value) + elif isinstance(self[key], Mapping): + for k, v in self[key].items(): + self[key][k] = self.substitute(v, interpolators, value) + else: + self[key] = self.substitute(self[key], interpolators, value) + if unsafe_eval and isinstance(self[key], str): + with suppress(SyntaxError): + self[key] = eval(self[key]) # pylint: disable=W0123 + return self + + @staticmethod + def find_placeholders(key, value, placeholders): + placeholder = find_placeholders(value) + if placeholder: + for index, name in enumerate(placeholder): + if name.startswith("."): + placeholder[index] = key.rsplit(".", 1)[0] + name + if key == name: + raise ValueError(f"Cannot interpolate {key} to itself.") + placeholders[key] = placeholder + + @staticmethod + def substitute(placeholder, interpolators, value): + try: + if len(value) == 1 and placeholder.startswith("${") and placeholder.endswith("}"): + return interpolators[value[0]] + return placeholder.replace("$", "").format(**interpolators) + except KeyError as exc: + raise ValueError(f"{exc} is not found in {interpolators}.") from None + + def merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> Self: + r""" + Merge `other` into `FlatDict`. + + Args: + *args: `Mapping` or `Sequence` to be merged. + overwrite: Whether to overwrite existing values. + **kwargs: `Mapping` to be merged. + + Returns: + self: + + **Alias**: + + + `union` + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> n = {'b': 'b', 'c': 'c', 'd': 'd'} + >>> d.merge(n).dict() + {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'} + >>> l = [('c', 3), ('d', 4)] + >>> d.merge(l).dict() + {'a': 1, 'b': 'b', 'c': 3, 'd': 4} + >>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict() # alias + {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'} + >>> d = FlatDict() + >>> d.merge({1: 1, 2: 2, 3:3}).dict() + {1: 1, 2: 2, 3: 3} + >>> d.merge(d.clone()).dict() + {1: 1, 2: 2, 3: 3} + >>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict() + {1: 1, 2: 2, 3: 3, 4: 4, 5: 5} + """ + + if len(args) == 1: + args = args[0] + if isinstance(args, (PathLike, str, bytes)): + args = self.load(args) # type: ignore[assignment] + warn( + "merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.", + PendingDeprecationWarning, + ) + self._merge(self, args, overwrite=overwrite) + elif len(args) > 1: + self._merge(self, args, overwrite=overwrite) + if kwargs: + self._merge(self, kwargs, overwrite=overwrite) + return self + + @staticmethod + def _merge(this: FlatDict, that: Iterable, overwrite: bool = True) -> Mapping: + if not that: + return this + if isinstance(that, Mapping): + that = that.items() + for key, value in that: + if key in this and isinstance(this[key], Mapping): + if isinstance(value, Mapping): + FlatDict._merge(this[key], value) + elif overwrite: + if isinstance(value, FlatDict): + this.set(key, value) + else: + this[key] = value + elif overwrite or key not in this: + this.set(key, value) + return this + + def union(self, *args: Any, **kwargs: Any) -> Self: + r""" + Alias of [`merge`][chanfig.FlatDict.merge]. + """ + return self.merge(*args, **kwargs) + + def merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> Self: + r""" + Merge content of `file` into `FlatDict`. + + Args: + file (File): + *args: Passed to [`load`][chanfig.FlatDict.load]. + **kwargs: Passed to [`load`][chanfig.FlatDict.load]. + + Returns: + self: + + Examples: + >>> d = FlatDict(a=1, b=1) + >>> d.merge_from_file("tests/test.yaml").dict() + {'a': 1, 'b': 2, 'c': 3} + """ + + return self.merge(self.load(file, *args, **kwargs)) + + def intersect(self, other: Mapping | Iterable | PathStr) -> Self: + r""" + Intersection of `FlatDict` and `other`. + + Args: + other (Mapping | Iterable | PathStr): + + Returns: + (FlatDict): + + **Alias**: + + + `inter` + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> n = {'b': 'b', 'c': 'c', 'd': 'd'} + >>> d.intersect(n).dict() + {} + >>> l = [('c', 3), ('d', 4)] + >>> d.intersect(l).dict() + {'c': 3} + >>> d.merge(l).intersect("tests/test.yaml").dict() + {'a': 1, 'b': 2, 'c': 3} + >>> d.intersect(1) + Traceback (most recent call last): + TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>. + >>> d.inter(FlatDict(b='b', c='c', d='d')).dict() # alias + {} + """ + + if isinstance(other, (PathLike, str, bytes)): + other = self.load(other) + if isinstance(other, (Mapping,)): + other = self.empty(other).items() + if not isinstance(other, Iterable): + raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.") + return self.empty(**{key: value for key, value in other if key in self and self[key] == value}) # type: ignore + + def inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self: + r""" + Alias of [`intersect`][chanfig.FlatDict.intersect]. + """ + return self.intersect(other, *args, **kwargs) + + def difference(self, other: Mapping | Iterable | PathStr) -> Self: + r""" + Difference between `FlatDict` and `other`. + + Args: + other: + + Returns: + (FlatDict): + + **Alias**: + + + `diff` + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> n = {'b': 'b', 'c': 'c', 'd': 'd'} + >>> d.difference(n).dict() + {'b': 'b', 'c': 'c', 'd': 'd'} + >>> l = [('c', 3), ('d', 4)] + >>> d.difference(l).dict() + {'d': 4} + >>> d.merge(l).difference("tests/test.yaml").dict() + {} + >>> d.difference(1) + Traceback (most recent call last): + TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>. + >>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict() # alias + {'b': 'b', 'c': 'c', 'd': 'd'} + """ + + if isinstance(other, (PathLike, str, bytes)): + other = self.load(other) + if isinstance(other, (Mapping,)): + other = self.empty(other).items() + if not isinstance(other, Iterable): + raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.") + return self.empty( + **{key: value for key, value in other if key not in self or self[key] != value} # type: ignore[misc] + ) + + def diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self: + r""" + Alias of [`difference`][chanfig.FlatDict.difference]. + """ + return self.difference(other, *args, **kwargs) + + def to(self, cls: str | TorchDevice | TorchDType) -> Self: # pragma: no cover + r""" + Convert values of `FlatDict` to target `cls`. + + Args: + cls (str | torch.device | torch.dtype): + + Returns: + self: + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.to(int) + Traceback (most recent call last): + TypeError: to() only support torch.dtype and torch.device, but got <class 'int'>. + """ + + # pylint: disable=C0103 + + if isinstance(cls, (str, TorchDevice, TorchDType)): + for k, v in self.all_items(): + if hasattr(v, "to"): + self[k] = v.to(cls) + return self + + raise TypeError(f"to() only support torch.dtype and torch.device, but got {cls}.") + + def cpu(self) -> Self: # pragma: no cover + r""" + Move all tensors to cpu. + + Returns: + self: + + Examples: + >>> import torch + >>> d = FlatDict(a=torch.tensor(1)) + >>> d.cpu().dict() # doctest: +SKIP + {'a': tensor(1, device='cpu')} + """ + + return self.to(TorchDevice("cpu")) + + def gpu(self) -> Self: # pragma: no cover + r""" + Move all tensors to gpu. + + Returns: + self: + + **Alias**: + + + `cuda` + + Examples: + >>> import torch + >>> d = FlatDict(a=torch.tensor(1)) + >>> d.gpu().dict() # doctest: +SKIP + {'a': tensor(1, device='cuda:0')} + >>> d.cuda().dict() # alias # doctest: +SKIP + {'a': tensor(1, device='cuda:0')} + """ + + return self.to(TorchDevice("cuda")) + + def cuda(self) -> Self: # pragma: no cover + r""" + Alias of [`gpu`][chanfig.FlatDict.gpu]. + """ + return self.gpu() + + def tpu(self) -> Self: # pragma: no cover + r""" + Move all tensors to tpu. + + Returns: + self: + + **Alias**: + + + `xla` + + Examples: + >>> import torch + >>> d = FlatDict(a=torch.tensor(1)) + >>> d.tpu().dict() # doctest: +SKIP + {'a': tensor(1, device='xla:0')} + >>> d.xla().dict() # alias # doctest: +SKIP + {'a': tensor(1, device='xla:0')} + """ + + return self.to(TorchDevice("xla")) + + def xla(self) -> Self: # pragma: no cover + r""" + Alias of [`tpu`][chanfig.FlatDict.tpu]. + """ + return self.tpu() + + def copy(self) -> Self: + r""" + Create a shallow copy of `FlatDict`. + + Returns: + (FlatDict): + + Examples: + >>> d = FlatDict(a=[]) + >>> d.setattr("name", "Chang") + >>> c = d.copy() + >>> c.dict() + {'a': []} + >>> d.a.append(1) + >>> c.dict() + {'a': [1]} + >>> c.getattr("name") + 'Chang' + """ + + return copy(self) + + def __deepcopy__(self, memo: Mapping | None = None) -> Self: + # pylint: disable=C0103 + + if memo is not None and id(self) in memo: + return memo[id(self)] + ret = self.empty() + ret.__dict__.update(deepcopy(self.__dict__)) + for k, v in self.items(): + if isinstance(v, FlatDict): + ret[k] = v.deepcopy(memo=memo) + else: + ret[k] = deepcopy(v) + return ret + + def deepcopy(self, memo: Mapping | None = None) -> Self: # pylint: disable=W0613 + r""" + Create a deep copy of `FlatDict`. + + Returns: + (FlatDict): + + **Alias**: + + + `clone` + + Examples: + >>> d = FlatDict(a=[]) + >>> d.setattr("name", "Chang") + >>> c = d.deepcopy() + >>> c.dict() + {'a': []} + >>> d.a.append(1) + >>> c.dict() + {'a': []} + >>> c.getattr("name") + 'Chang' + >>> d == d.clone() # alias + True + """ + + return deepcopy(self) + + def clone(self, memo: Mapping | None = None) -> Self: + r""" + Alias of [`deepcopy`][chanfig.FlatDict.deepcopy]. + """ + return self.deepcopy(memo=memo) + + def save( # pylint: disable=W1113 + self, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment] + ) -> None: + r""" + Save `FlatDict` to file. + + Raises: + ValueError: If save to `IO` and `method` is not specified. + TypeError: If save to unsupported extension. + + **Alias**: + + + `save` + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.save("tests/test.yaml") + >>> d.save("test.conf") + Traceback (most recent call last): + TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf. + >>> with open("test.yaml", "w") as f: + ... d.save(f) + Traceback (most recent call last): + ValueError: `method` must be specified when saving to IO. + """ + + if method is None: + if isinstance(file, (IOBase, IO)): + raise ValueError("`method` must be specified when saving to IO.") + method = splitext(file)[-1][1:] + extension = method.lower() + if extension in YAML: + return self.yaml(file=file, *args, **kwargs) # type: ignore[misc] # noqa: B026 + if extension in JSON: + return self.json(file=file, *args, **kwargs) # type: ignore[misc] # noqa: B026 + raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.") + + def dump( # pylint: disable=W1113 + self, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment] + ) -> None: + r""" + Alias of [`save`][chanfig.FlatDict.save]. + """ + return self.save(file, method, *args, **kwargs) + + @classmethod + def load( # pylint: disable=W1113 + cls, file: File, method: str = None, *args: Any, **kwargs: Any # type: ignore[assignment] + ) -> Self: + """ + Load `FlatDict` from file. + + Args: + file: File to load from. + method: File type, should be in `JSON` or `YAML`. + + Returns: + (FlatDict): + + Raises: + ValueError: If load from `IO` and `method` is not specified. + TypeError: If dump to unsupported extension. + + Examples: + >>> d = FlatDict.load("tests/test.yaml") + >>> d.dict() + {'a': 1, 'b': 2, 'c': 3} + >>> d.load("tests/test.conf") + Traceback (most recent call last): + TypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf. + >>> with open("tests/test.yaml") as f: + ... d.load(f) + Traceback (most recent call last): + ValueError: `method` must be specified when loading from IO. + """ + + if method is None: + if isinstance(file, (IOBase, IO)): + raise ValueError("`method` must be specified when loading from IO.") + method = splitext(file)[-1][1:] + extension = method.lower() + if extension in JSON: + return cls.from_json(file, *args, **kwargs) + if extension in YAML: + return cls.from_yaml(file, *args, **kwargs) + raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.") + + def json(self, file: File, *args: Any, **kwargs: Any) -> None: + r""" + 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: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.json("tests/test.json") + """ + + with self.open(file, mode="w") as fp: # pylint: disable=C0103 + fp.write(self.jsons(*args, **kwargs)) + + @classmethod + def from_json(cls, file: File, *args: Any, **kwargs: Any) -> Self: + r""" + 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: + (FlatDict): + + Examples: + >>> d = FlatDict.from_json('tests/test.json') + >>> d.dict() + {'a': 1, 'b': 2, 'c': 3} + """ + + with cls.open(file) as fp: # pylint: disable=C0103 + if isinstance(file, (IOBase, IO)): + return cls.from_jsons(fp.getvalue(), *args, **kwargs) # type: ignore[union-attr] + return cls.from_jsons(fp.read(), *args, **kwargs) + + def jsons(self, *args: Any, **kwargs: Any) -> str: + r""" + Dump `FlatDict` to json string. + + Returns: + (str): + + Examples: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.jsons() + '{\n "a": 1,\n "b": 2,\n "c": 3\n}' + """ + + kwargs.setdefault("cls", JsonEncoder) + kwargs.setdefault("indent", self.getattr("indent", 2)) + return json_dumps(self.dict(), *args, **kwargs) + + @classmethod + def from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> Self: + r""" + Construct `FlatDict` from json string. + + Returns: + (FlatDict): + + Examples: + >>> FlatDict.from_jsons('{\n "a": 1,\n "b": 2,\n "c": 3\n}').dict() + {'a': 1, 'b': 2, 'c': 3} + >>> FlatDict.from_jsons('[["a", 1], ["b", 2], ["c", 3]]').dict() + {'a': 1, 'b': 2, 'c': 3} + >>> FlatDict.from_jsons('[{"a": 1}, {"b": 2}, {"c": 3}]') + [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)] + """ + + return cls.from_dict(json_loads(string, *args, **kwargs)) + + def yaml(self, file: File, *args: Any, **kwargs: Any) -> None: + r""" + 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: + >>> d = FlatDict(a=1, b=2, c=3) + >>> d.yaml("tests/test.yaml") + """ + + with self.open(file, mode="w") as fp: # pylint: disable=C0103 + self.yamls(fp, *args, **kwargs) + + @classmethod + def from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> Self: + r""" + 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: + (FlatDict): + + Examples: + >>> FlatDict.from_yaml('tests/test.yaml').dict() + {'a': 1, 'b': 2, 'c': 3} + """ + + kwargs.setdefault("Loader", YamlLoader) + with cls.open(file) as fp: # pylint: disable=C0103 + if isinstance(file, (IOBase, IO)): + return cls.from_yamls(fp.getvalue(), *args, **kwargs) # type: ignore[union-attr] + return cls.from_dict(yaml_load(fp, *args, **kwargs)) + + def yamls(self, *args: Any, **kwargs: Any) -> str: + r""" + Dump `FlatDict` to yaml string. + + Returns: + (str): + + Examples: + >>> FlatDict(a=1, b=2, c=3).yamls() + 'a: 1\nb: 2\nc: 3\n' + """ + + kwargs.setdefault("Dumper", YamlDumper) + kwargs.setdefault("indent", self.getattr("indent", 2)) + return yaml_dump(self.dict(), *args, **kwargs) + + @classmethod + def from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> Self: + r""" + Construct `FlatDict` from yaml string. + + Returns: + (FlatDict): + + Examples: + >>> FlatDict.from_yamls('a: 1\nb: 2\nc: 3\n').dict() + {'a': 1, 'b': 2, 'c': 3} + >>> FlatDict.from_yamls('- - a\n - 1\n- - b\n - 2\n- - c\n - 3\n').dict() + {'a': 1, 'b': 2, 'c': 3} + >>> FlatDict.from_yamls('- a: 1\n- b: 2\n- c: 3\n') + [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)] + """ + + kwargs.setdefault("Loader", SafeLoader) + return cls.from_dict(yaml_load(string, *args, **kwargs)) + + @staticmethod + @contextmanager + def open(file: File, *args: Any, encoding: str = "utf-8", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]: + r""" + 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. + + Args: + file: File path or IO. + *args: Additional arguments passed to `open`. + Defaults to (). + **kwargs: Any + Additional keyword arguments passed to `open`. + Defaults to {}. + + Yields: + (Generator[IOBase | IO, Any, Any]): + + Examples: + >>> with FlatDict.open("tests/test.yaml") as fp: + ... print(fp.read()) + a: 1 + b: 2 + c: 3 + <BLANKLINE> + >>> io = open("tests/test.yaml") + >>> with FlatDict.open(io) as fp: + ... print(fp.read()) + a: 1 + b: 2 + c: 3 + <BLANKLINE> + >>> with FlatDict.open(123, mode="w") as fp: + ... print(fp.read()) + Traceback (most recent call last): + TypeError: expected str, bytes, os.PathLike, IO or IOBase, not int + """ + + if isinstance(file, (IOBase, IO)): + yield file + elif isinstance(file, (PathLike, str, bytes)): + try: + file = open(file, *args, encoding=encoding, **kwargs) # type: ignore[call-overload] # noqa: SIM115 + yield file # type: ignore[misc] + finally: + with suppress(Exception): + file.close() # type: ignore[union-attr] + else: + raise TypeError(f"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}") + + @classmethod + def empty(cls, *args: Any, **kwargs: Any) -> Self: + r""" + 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: + (FlatDict): + + See Also: + [`empty_like`][chanfig.FlatDict.empty_like] + + Examples: + >>> d = FlatDict(a=[]) + >>> c = d.empty() + >>> c.dict() + {} + """ + + empty = cls.__new__(cls) + empty.merge(*args, **kwargs) # pylint: disable=W0212 + return empty + + def empty_like(self, *args: Any, **kwargs: Any) -> Self: + r""" + Initialise an empty copy of `FlatDict`. + + This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`. + + For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this + method. + + Returns: + (FlatDict): + + See Also: + [`empty`][chanfig.FlatDict.empty] + + Examples: + >>> d = FlatDict(a=[]) + >>> d.setattr("name", "Chang") + >>> c = d.empty_like() + >>> c.dict() + {} + >>> c.getattr("name") + 'Chang' + """ + + empty = self.empty(*args, **kwargs) + empty.__dict__.update(self.__dict__) + return empty + + def all_keys(self) -> Generator: + r""" + Equivalent to `keys`. + + This method is provided solely to make methods work on both `FlatDict` and `NestedDict`. + + See Also: + [`all_keys`][chanfig.NestedDict.all_keys] + """ + yield from self.keys() + + def all_values(self) -> Generator: + r""" + Equivalent to `keys`. + + This method is provided solely to make methods work on both `FlatDict` and `NestedDict`. + + See Also: + [`all_values`][chanfig.NestedDict.all_values] + """ + yield from self.values() + + def all_items(self) -> Generator: + r""" + Equivalent to `keys`. + + This method is provided solely to make methods work on both `FlatDict` and `NestedDict`. + + See Also: + [`all_items`][chanfig.NestedDict.all_items] + """ + yield from self.items() + + def dropnull(self) -> Self: + r""" + Drop key-value pairs with `Null` value. + + Returns: + (FlatDict): + + **Alias**: + + + `dropna` + + Examples: + >>> d = FlatDict(a=Null, b=Null, c=3) + >>> d.dict() + {'a': Null, 'b': Null, 'c': 3} + >>> d.dropnull().dict() + {'c': 3} + >>> d.dropna().dict() # alias + {'c': 3} + """ + + return self.empty({k: v for k, v in self.all_items() if v is not Null}) + + def dropna(self) -> Self: + r""" + Alias of [`dropnull`][chanfig.FlatDict.dropnull]. + """ + return self.dropnull() + + @staticmethod + def extra_repr() -> str: # pylint: disable=C0116 + return "" + + def __repr__(self) -> str: + extra_lines = [] + extra_repr = self.extra_repr() + # empty string will be split into list [''] + if extra_repr: + extra_lines = extra_repr.split("\n") + child_lines = [] + for key, value in self.items(): + key_repr = repr(key) + value_repr = repr(value) + value_repr = self._add_indent(value_repr) + child_lines.append(f"({key_repr}): {value_repr}") + # child_lines.append(f"{key_repr}: {value_repr}") + lines = extra_lines + child_lines + + main_repr = self.__class__.__name__ + "(" + if lines: + # simple one-liner info, which most builtin Modules will use + if len(extra_lines) == 1 and not child_lines: + main_repr += extra_lines[0] + elif len(child_lines) == 1 and not extra_lines and len(child_lines[0]) < 10: + main_repr += child_lines[0] + else: + main_repr += "\n " + "\n ".join(lines) + "\n" + + main_repr += ")" + return main_repr + + def _add_indent(self, text: str) -> str: + lines = text.split("\n") + # don't do anything for single-line stuff + if len(lines) == 1: + return text + first = lines.pop(0) + lines = [(self.getattr("indent", 2) * " ") + line for line in lines] + text = "\n".join(lines) + text = first + "\n" + text + return text + + def __format__(self, format_spec: str) -> str: + return repr(self.empty({k: v.__format__(format_spec) for k, v in self.all_items()})) + + def __hash__(self): + return hash(frozenset(self.items())) + + def _ipython_display_(self): # pragma: no cover + return repr(self) + + def _ipython_canary_method_should_not_exist_(self): # pragma: no cover + return None + + def aihwerij235234ljsdnp34ksodfipwoe234234jlskjdf(self): # pragma: no cover + return None + + def __rich__(self): # pragma: no cover + return self.__repr__()

@@ -4358,8 +4360,7 @@

Source code in chanfig/flat_dict.py -
Python
1433
-1434
+              
Python
1434
 1435
 1436
 1437
@@ -4367,16 +4368,17 @@ 

1439 1440 1441 -1442

def all_items(self) -> Generator:
-    r"""
-    Equivalent to `keys`.
-
-    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.
-
-    See Also:
-        [`all_items`][chanfig.NestedDict.all_items]
-    """
-    yield from self.items()
+1442
+1443
def all_items(self) -> Generator:
+    r"""
+    Equivalent to `keys`.
+
+    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.
+
+    See Also:
+        [`all_items`][chanfig.NestedDict.all_items]
+    """
+    yield from self.items()
 
@@ -4404,8 +4406,7 @@

Source code in chanfig/flat_dict.py -
Python
1411
-1412
+              
Python
1412
 1413
 1414
 1415
@@ -4413,16 +4414,17 @@ 

1417 1418 1419 -1420

def all_keys(self) -> Generator:
-    r"""
-    Equivalent to `keys`.
-
-    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.
-
-    See Also:
-        [`all_keys`][chanfig.NestedDict.all_keys]
-    """
-    yield from self.keys()
+1420
+1421
def all_keys(self) -> Generator:
+    r"""
+    Equivalent to `keys`.
+
+    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.
+
+    See Also:
+        [`all_keys`][chanfig.NestedDict.all_keys]
+    """
+    yield from self.keys()
 
@@ -4450,8 +4452,7 @@

Source code in chanfig/flat_dict.py -
Python
1422
-1423
+              
Python
1423
 1424
 1425
 1426
@@ -4459,16 +4460,17 @@ 

1428 1429 1430 -1431

def all_values(self) -> Generator:
-    r"""
-    Equivalent to `keys`.
-
-    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.
-
-    See Also:
-        [`all_values`][chanfig.NestedDict.all_values]
-    """
-    yield from self.values()
+1431
+1432
def all_values(self) -> Generator:
+    r"""
+    Equivalent to `keys`.
+
+    This method is provided solely to make methods work on both `FlatDict` and `NestedDict`.
+
+    See Also:
+        [`all_values`][chanfig.NestedDict.all_values]
+    """
+    yield from self.values()
 
@@ -4490,15 +4492,15 @@

Source code in chanfig/flat_dict.py -
Python
1065
-1066
+              
Python
def clone(self, memo: Mapping | None = None) -> Self:
-    r"""
-    Alias of [`deepcopy`][chanfig.FlatDict.deepcopy].
-    """
-    return self.deepcopy(memo=memo)
+1069
+1070
def clone(self, memo: Mapping | None = None) -> Self:
+    r"""
+    Alias of [`deepcopy`][chanfig.FlatDict.deepcopy].
+    """
+    return self.deepcopy(memo=memo)
 
@@ -4557,8 +4559,7 @@

Source code in chanfig/flat_dict.py -
Python
1001
-1002
+              
Python
1002
 1003
 1004
 1005
@@ -4577,27 +4578,28 @@ 

1018 1019 1020 -1021

def copy(self) -> Self:
-    r"""
-    Create a shallow copy of `FlatDict`.
-
-    Returns:
-        (FlatDict):
-
-    Examples:
-        >>> d = FlatDict(a=[])
-        >>> d.setattr("name", "Chang")
-        >>> c = d.copy()
-        >>> c.dict()
-        {'a': []}
-        >>> d.a.append(1)
-        >>> c.dict()
-        {'a': [1]}
-        >>> c.getattr("name")
-        'Chang'
-    """
-
-    return copy(self)
+1021
+1022
def copy(self) -> Self:
+    r"""
+    Create a shallow copy of `FlatDict`.
+
+    Returns:
+        (FlatDict):
+
+    Examples:
+        >>> d = FlatDict(a=[])
+        >>> d.setattr("name", "Chang")
+        >>> c = d.copy()
+        >>> c.dict()
+        {'a': []}
+        >>> d.a.append(1)
+        >>> c.dict()
+        {'a': [1]}
+        >>> c.getattr("name")
+        'Chang'
+    """
+
+    return copy(self)
 
@@ -4650,8 +4652,7 @@

Source code in chanfig/flat_dict.py -
Python
929
-930
+              
Python
930
 931
 932
 933
@@ -4664,21 +4665,22 @@ 

940 941 942 -943

def cpu(self) -> Self:  # pragma: no cover
-    r"""
-    Move all tensors to cpu.
-
-    Returns:
-        self:
-
-    Examples:
-        >>> import torch
-        >>> d = FlatDict(a=torch.tensor(1))
-        >>> d.cpu().dict()  # doctest: +SKIP
-        {'a': tensor(1, device='cpu')}
-    """
-
-    return self.to(TorchDevice("cpu"))
+943
+944
def cpu(self) -> Self:  # pragma: no cover
+    r"""
+    Move all tensors to cpu.
+
+    Returns:
+        self:
+
+    Examples:
+        >>> import torch
+        >>> d = FlatDict(a=torch.tensor(1))
+        >>> d.cpu().dict()  # doctest: +SKIP
+        {'a': tensor(1, device='cpu')}
+    """
+
+    return self.to(TorchDevice("cpu"))
 
@@ -4700,15 +4702,15 @@

Source code in chanfig/flat_dict.py -
Python
967
-968
+              
Python
def cuda(self) -> Self:  # pragma: no cover
-    r"""
-    Alias of [`gpu`][chanfig.FlatDict.gpu].
-    """
-    return self.gpu()
+971
+972
def cuda(self) -> Self:  # pragma: no cover
+    r"""
+    Alias of [`gpu`][chanfig.FlatDict.gpu].
+    """
+    return self.gpu()
 
@@ -4773,8 +4775,7 @@

Source code in chanfig/flat_dict.py -
Python
1037
-1038
+              
Python
1038
 1039
 1040
 1041
@@ -4799,33 +4800,34 @@ 

1060 1061 1062 -1063

def deepcopy(self, memo: Mapping | None = None) -> Self:  # pylint: disable=W0613
-    r"""
-    Create a deep copy of `FlatDict`.
-
-    Returns:
-        (FlatDict):
-
-    **Alias**:
-
-    + `clone`
-
-    Examples:
-        >>> d = FlatDict(a=[])
-        >>> d.setattr("name", "Chang")
-        >>> c = d.deepcopy()
-        >>> c.dict()
-        {'a': []}
-        >>> d.a.append(1)
-        >>> c.dict()
-        {'a': []}
-        >>> c.getattr("name")
-        'Chang'
-        >>> d == d.clone()  # alias
-        True
-    """
-
-    return deepcopy(self)
+1063
+1064
def deepcopy(self, memo: Mapping | None = None) -> Self:  # pylint: disable=W0613
+    r"""
+    Create a deep copy of `FlatDict`.
+
+    Returns:
+        (FlatDict):
+
+    **Alias**:
+
+    + `clone`
+
+    Examples:
+        >>> d = FlatDict(a=[])
+        >>> d.setattr("name", "Chang")
+        >>> c = d.deepcopy()
+        >>> c.dict()
+        {'a': []}
+        >>> d.a.append(1)
+        >>> c.dict()
+        {'a': []}
+        >>> c.getattr("name")
+        'Chang'
+        >>> d == d.clone()  # alias
+        True
+    """
+
+    return deepcopy(self)
 
@@ -4889,8 +4891,7 @@

Source code in chanfig/flat_dict.py -
Python
444
-445
+              
Python
445
 446
 447
 448
@@ -4909,27 +4910,28 @@ 

461 462 463 -464

def delattr(self, name: str) -> None:
-    r"""
-    Delete attribute of `FlatDict`.
-
-    Note that it won't delete values in `FlatDict`.
-
-    Args:
-        name:
-
-    Examples:
-        >>> d = FlatDict()
-        >>> d.setattr('name', 'chang')
-        >>> d.getattr('name')
-        'chang'
-        >>> d.delattr('name')
-        >>> d.getattr('name')
-        Traceback (most recent call last):
-        AttributeError: 'FlatDict' object has no attribute 'name'
-    """
-
-    del self.__dict__[name]
+464
+465
def delattr(self, name: str) -> None:
+    r"""
+    Delete attribute of `FlatDict`.
+
+    Note that it won't delete values in `FlatDict`.
+
+    Args:
+        name:
+
+    Examples:
+        >>> d = FlatDict()
+        >>> d.setattr('name', 'chang')
+        >>> d.getattr('name')
+        'chang'
+        >>> d.delattr('name')
+        >>> d.getattr('name')
+        Traceback (most recent call last):
+        AttributeError: 'FlatDict' object has no attribute 'name'
+    """
+
+    del self.__dict__[name]
 
@@ -5142,8 +5144,7 @@

Source code in chanfig/flat_dict.py -
Python
493
-494
+              
Python
494
 495
 496
 497
@@ -5165,30 +5166,31 @@ 

513 514 515 -516

def dict(self, flatten: bool = False) -> Mapping | Sequence | Set:
-    r"""
-    Convert `FlatDict` to other `Mapping`.
-
-    Args:
-        flatten: Whether to flatten [`NestedDict`][chanfig.NestedDict].
-
-    Returns:
-        (Mapping):
-
-    See Also:
-        [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`.
-
-    **Alias**:
-
-    + `to_dict`
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.dict()
-        {'a': 1, 'b': 2, 'c': 3}
-    """
-
-    return to_dict(self, flatten)
+516
+517
def dict(self, flatten: bool = False) -> Mapping | Sequence | Set:
+    r"""
+    Convert `FlatDict` to other `Mapping`.
+
+    Args:
+        flatten: Whether to flatten [`NestedDict`][chanfig.NestedDict].
+
+    Returns:
+        (Mapping):
+
+    See Also:
+        [`to_dict`][chanfig.flat_dict.to_dict]: Implementation of `dict`.
+
+    **Alias**:
+
+    + `to_dict`
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.dict()
+        {'a': 1, 'b': 2, 'c': 3}
+    """
+
+    return to_dict(self, flatten)
 
@@ -5210,15 +5212,15 @@

Source code in chanfig/flat_dict.py -
Python
896
-897
+              
Python
def diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Alias of [`difference`][chanfig.FlatDict.difference].
-    """
-    return self.difference(other, *args, **kwargs)
+900
+901
def diff(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Alias of [`difference`][chanfig.FlatDict.difference].
+    """
+    return self.difference(other, *args, **kwargs)
 
@@ -5314,8 +5316,7 @@

Source code in chanfig/flat_dict.py -
Python
855
-856
+              
Python
856
 857
 858
 859
@@ -5353,46 +5354,47 @@ 

891 892 893 -894

def difference(self, other: Mapping | Iterable | PathStr) -> Self:
-    r"""
-    Difference between `FlatDict` and `other`.
-
-    Args:
-        other:
-
-    Returns:
-        (FlatDict):
-
-    **Alias**:
-
-    + `diff`
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}
-        >>> d.difference(n).dict()
-        {'b': 'b', 'c': 'c', 'd': 'd'}
-        >>> l = [('c', 3), ('d', 4)]
-        >>> d.difference(l).dict()
-        {'d': 4}
-        >>> d.merge(l).difference("tests/test.yaml").dict()
-        {}
-        >>> d.difference(1)
-        Traceback (most recent call last):
-        TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.
-        >>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict()  # alias
-        {'b': 'b', 'c': 'c', 'd': 'd'}
-    """
-
-    if isinstance(other, (PathLike, str, bytes)):
-        other = self.load(other)
-    if isinstance(other, (Mapping,)):
-        other = self.empty(other).items()
-    if not isinstance(other, Iterable):
-        raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.")
-    return self.empty(
-        **{key: value for key, value in other if key not in self or self[key] != value}  # type: ignore[misc]
-    )
+894
+895
def difference(self, other: Mapping | Iterable | PathStr) -> Self:
+    r"""
+    Difference between `FlatDict` and `other`.
+
+    Args:
+        other:
+
+    Returns:
+        (FlatDict):
+
+    **Alias**:
+
+    + `diff`
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}
+        >>> d.difference(n).dict()
+        {'b': 'b', 'c': 'c', 'd': 'd'}
+        >>> l = [('c', 3), ('d', 4)]
+        >>> d.difference(l).dict()
+        {'d': 4}
+        >>> d.merge(l).difference("tests/test.yaml").dict()
+        {}
+        >>> d.difference(1)
+        Traceback (most recent call last):
+        TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.
+        >>> FlatDict(a=1, b=1, c=1).diff(FlatDict(b='b', c='c', d='d')).dict()  # alias
+        {'b': 'b', 'c': 'c', 'd': 'd'}
+    """
+
+    if isinstance(other, (PathLike, str, bytes)):
+        other = self.load(other)
+    if isinstance(other, (Mapping,)):
+        other = self.empty(other).items()
+    if not isinstance(other, Iterable):
+        raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.")
+    return self.empty(
+        **{key: value for key, value in other if key not in self or self[key] != value}  # type: ignore[misc]
+    )
 
@@ -5414,15 +5416,15 @@

Source code in chanfig/flat_dict.py -
Python
1467
-1468
+              
Python
def dropna(self) -> Self:
-    r"""
-    Alias of [`dropnull`][chanfig.FlatDict.dropnull].
-    """
-    return self.dropnull()
+1471
+1472
def dropna(self) -> Self:
+    r"""
+    Alias of [`dropnull`][chanfig.FlatDict.dropnull].
+    """
+    return self.dropnull()
 
@@ -5482,8 +5484,7 @@

Source code in chanfig/flat_dict.py -
Python
1444
-1445
+              
Python
1445
 1446
 1447
 1448
@@ -5503,28 +5504,29 @@ 

1462 1463 1464 -1465

def dropnull(self) -> Self:
-    r"""
-    Drop key-value pairs with `Null` value.
-
-    Returns:
-        (FlatDict):
-
-    **Alias**:
-
-    + `dropna`
-
-    Examples:
-        >>> d = FlatDict(a=Null, b=Null, c=3)
-        >>> d.dict()
-        {'a': Null, 'b': Null, 'c': 3}
-        >>> d.dropnull().dict()
-        {'c': 3}
-        >>> d.dropna().dict()  # alias
-        {'c': 3}
-    """
-
-    return self.empty({k: v for k, v in self.all_items() if v is not Null})
+1465
+1466
def dropnull(self) -> Self:
+    r"""
+    Drop key-value pairs with `Null` value.
+
+    Returns:
+        (FlatDict):
+
+    **Alias**:
+
+    + `dropna`
+
+    Examples:
+        >>> d = FlatDict(a=Null, b=Null, c=3)
+        >>> d.dict()
+        {'a': Null, 'b': Null, 'c': 3}
+        >>> d.dropnull().dict()
+        {'c': 3}
+        >>> d.dropna().dict()  # alias
+        {'c': 3}
+    """
+
+    return self.empty({k: v for k, v in self.all_items() if v is not Null})
 
@@ -5546,19 +5548,19 @@

Source code in chanfig/flat_dict.py -
Python
1108
-1109
+              
Python
def dump(  # pylint: disable=W1113
-    self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]
-) -> None:
-    r"""
-    Alias of [`save`][chanfig.FlatDict.save].
-    """
-    return self.save(file, method, *args, **kwargs)
+1114
+1115
def dump(  # pylint: disable=W1113
+    self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]
+) -> None:
+    r"""
+    Alias of [`save`][chanfig.FlatDict.save].
+    """
+    return self.save(file, method, *args, **kwargs)
 
@@ -5623,8 +5625,7 @@

Source code in chanfig/flat_dict.py -
Python
1355
-1356
+              
Python
1356
 1357
 1358
 1359
@@ -5648,32 +5649,33 @@ 

1377 1378 1379 -1380

@classmethod
-def empty(cls, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    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:
-        (FlatDict):
-
-    See Also:
-        [`empty_like`][chanfig.FlatDict.empty_like]
-
-    Examples:
-        >>> d = FlatDict(a=[])
-        >>> c = d.empty()
-        >>> c.dict()
-        {}
-    """
-
-    empty = cls.__new__(cls)
-    empty.merge(*args, **kwargs)  # pylint: disable=W0212
-    return empty
+1380
+1381
@classmethod
+def empty(cls, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    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:
+        (FlatDict):
+
+    See Also:
+        [`empty_like`][chanfig.FlatDict.empty_like]
+
+    Examples:
+        >>> d = FlatDict(a=[])
+        >>> c = d.empty()
+        >>> c.dict()
+        {}
+    """
+
+    empty = cls.__new__(cls)
+    empty.merge(*args, **kwargs)  # pylint: disable=W0212
+    return empty
 
@@ -5737,8 +5739,7 @@

Source code in chanfig/flat_dict.py -
Python
1382
-1383
+              
Python
1383
 1384
 1385
 1386
@@ -5764,34 +5765,35 @@ 

1406 1407 1408 -1409

def empty_like(self, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Initialise an empty copy of `FlatDict`.
-
-    This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`.
-
-    For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this
-    method.
-
-    Returns:
-        (FlatDict):
-
-    See Also:
-        [`empty`][chanfig.FlatDict.empty]
-
-    Examples:
-        >>> d = FlatDict(a=[])
-        >>> d.setattr("name", "Chang")
-        >>> c = d.empty_like()
-        >>> c.dict()
-        {}
-        >>> c.getattr("name")
-        'Chang'
-    """
-
-    empty = self.empty(*args, **kwargs)
-    empty.__dict__.update(self.__dict__)
-    return empty
+1409
+1410
def empty_like(self, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Initialise an empty copy of `FlatDict`.
+
+    This method will preserve everything in `FlatDict.__class__.__dict__` and `FlatDict.__dict__`.
+
+    For example, `property`s are saved in `__dict__`, they will keep their original reference after calling this
+    method.
+
+    Returns:
+        (FlatDict):
+
+    See Also:
+        [`empty`][chanfig.FlatDict.empty]
+
+    Examples:
+        >>> d = FlatDict(a=[])
+        >>> d.setattr("name", "Chang")
+        >>> c = d.empty_like()
+        >>> c.dict()
+        {}
+        >>> c.getattr("name")
+        'Chang'
+    """
+
+    empty = self.empty(*args, **kwargs)
+    empty.__dict__.update(self.__dict__)
+    return empty
 
@@ -5838,8 +5840,7 @@

Source code in chanfig/flat_dict.py -
Python
525
-526
+              
Python
526
 527
 528
 529
@@ -5874,43 +5875,44 @@ 

558 559 560 -561

@classmethod
-def from_dict(cls, obj: Mapping | Sequence) -> Any:  # pylint: disable=R0911
-    r"""
-    Convert `Mapping` or `Sequence` to `FlatDict`.
-
-    Examples:
-        >>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3})
-        FlatDict(
-          ('a'): 1
-          ('b'): 2
-          ('c'): 3
-        )
-        >>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)])
-        FlatDict(
-          ('a'): 1
-          ('b'): 2
-          ('c'): 3
-        )
-        >>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}])
-        [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]
-        >>> FlatDict.from_dict({1, 2, 3})
-        Traceback (most recent call last):
-        TypeError: Expected Mapping or Sequence, but got <class 'set'>.
-    """
-
-    if obj is None:
-        return cls()
-    if issubclass(cls, FlatDict):
-        cls = cls.empty  # type: ignore[assignment] # pylint: disable=W0642
-    if isinstance(obj, Mapping):
-        return cls(obj)
-    if isinstance(obj, Sequence):
-        try:
-            return cls(obj)
-        except ValueError:
-            return [cls(json) for json in obj]
-    raise TypeError(f"Expected Mapping or Sequence, but got {type(obj)}.")
+561
+562
@classmethod
+def from_dict(cls, obj: Mapping | Sequence) -> Any:  # pylint: disable=R0911
+    r"""
+    Convert `Mapping` or `Sequence` to `FlatDict`.
+
+    Examples:
+        >>> FlatDict.from_dict({'a': 1, 'b': 2, 'c': 3})
+        FlatDict(
+          ('a'): 1
+          ('b'): 2
+          ('c'): 3
+        )
+        >>> FlatDict.from_dict([('a', 1), ('b', 2), ('c', 3)])
+        FlatDict(
+          ('a'): 1
+          ('b'): 2
+          ('c'): 3
+        )
+        >>> FlatDict.from_dict([{'a': 1}, {'b': 2}, {'c': 3}])
+        [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]
+        >>> FlatDict.from_dict({1, 2, 3})
+        Traceback (most recent call last):
+        TypeError: Expected Mapping or Sequence, but got <class 'set'>.
+    """
+
+    if obj is None:
+        return cls()
+    if issubclass(cls, FlatDict):
+        cls = cls.empty  # type: ignore[assignment] # pylint: disable=W0642
+    if isinstance(obj, Mapping):
+        return cls(obj)
+    if isinstance(obj, Sequence):
+        try:
+            return cls(obj)
+        except ValueError:
+            return [cls(json) for json in obj]
+    raise TypeError(f"Expected Mapping or Sequence, but got {type(obj)}.")
 
@@ -5968,8 +5970,7 @@

Source code in chanfig/flat_dict.py -
Python
1173
-1174
+              
Python
1174
 1175
 1176
 1177
@@ -5988,27 +5989,28 @@ 

1190 1191 1192 -1193

@classmethod
-def from_json(cls, file: File, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    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:
-        (FlatDict):
-
-    Examples:
-        >>> d = FlatDict.from_json('tests/test.json')
-        >>> d.dict()
-        {'a': 1, 'b': 2, 'c': 3}
-    """
-
-    with cls.open(file) as fp:  # pylint: disable=C0103
-        if isinstance(file, (IOBase, IO)):
-            return cls.from_jsons(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]
-        return cls.from_jsons(fp.read(), *args, **kwargs)
+1193
+1194
@classmethod
+def from_json(cls, file: File, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    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:
+        (FlatDict):
+
+    Examples:
+        >>> d = FlatDict.from_json('tests/test.json')
+        >>> d.dict()
+        {'a': 1, 'b': 2, 'c': 3}
+    """
+
+    with cls.open(file) as fp:  # pylint: disable=C0103
+        if isinstance(file, (IOBase, IO)):
+            return cls.from_jsons(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]
+        return cls.from_jsons(fp.read(), *args, **kwargs)
 
@@ -6067,8 +6069,7 @@

Source code in chanfig/flat_dict.py -
Python
1212
-1213
+              
Python
1213
 1214
 1215
 1216
@@ -6084,24 +6085,25 @@ 

1226 1227 1228 -1229

@classmethod
-def from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Construct `FlatDict` from json string.
-
-    Returns:
-        (FlatDict):
-
-    Examples:
-        >>> FlatDict.from_jsons('{\n  "a": 1,\n  "b": 2,\n  "c": 3\n}').dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> FlatDict.from_jsons('[["a", 1], ["b", 2], ["c", 3]]').dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> FlatDict.from_jsons('[{"a": 1}, {"b": 2}, {"c": 3}]')
-        [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]
-    """
-
-    return cls.from_dict(json_loads(string, *args, **kwargs))
+1229
+1230
@classmethod
+def from_jsons(cls, string: str, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Construct `FlatDict` from json string.
+
+    Returns:
+        (FlatDict):
+
+    Examples:
+        >>> FlatDict.from_jsons('{\n  "a": 1,\n  "b": 2,\n  "c": 3\n}').dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> FlatDict.from_jsons('[["a", 1], ["b", 2], ["c", 3]]').dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> FlatDict.from_jsons('[{"a": 1}, {"b": 2}, {"c": 3}]')
+        [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]
+    """
+
+    return cls.from_dict(json_loads(string, *args, **kwargs))
 
@@ -6158,8 +6160,7 @@

Source code in chanfig/flat_dict.py -
Python
1246
-1247
+              
Python
1247
 1248
 1249
 1250
@@ -6178,27 +6179,28 @@ 

1263 1264 1265 -1266

@classmethod
-def from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    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:
-        (FlatDict):
-
-    Examples:
-        >>> FlatDict.from_yaml('tests/test.yaml').dict()
-        {'a': 1, 'b': 2, 'c': 3}
-    """
-
-    kwargs.setdefault("Loader", YamlLoader)
-    with cls.open(file) as fp:  # pylint: disable=C0103
-        if isinstance(file, (IOBase, IO)):
-            return cls.from_yamls(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]
-        return cls.from_dict(yaml_load(fp, *args, **kwargs))
+1266
+1267
@classmethod
+def from_yaml(cls, file: File, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    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:
+        (FlatDict):
+
+    Examples:
+        >>> FlatDict.from_yaml('tests/test.yaml').dict()
+        {'a': 1, 'b': 2, 'c': 3}
+    """
+
+    kwargs.setdefault("Loader", YamlLoader)
+    with cls.open(file) as fp:  # pylint: disable=C0103
+        if isinstance(file, (IOBase, IO)):
+            return cls.from_yamls(fp.getvalue(), *args, **kwargs)  # type: ignore[union-attr]
+        return cls.from_dict(yaml_load(fp, *args, **kwargs))
 
@@ -6257,8 +6259,7 @@

Source code in chanfig/flat_dict.py -
Python
1284
-1285
+              
Python
1285
 1286
 1287
 1288
@@ -6275,25 +6276,26 @@ 

1299 1300 1301 -1302

@classmethod
-def from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Construct `FlatDict` from yaml string.
-
-    Returns:
-        (FlatDict):
-
-    Examples:
-        >>> FlatDict.from_yamls('a: 1\nb: 2\nc: 3\n').dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> FlatDict.from_yamls('- - a\n  - 1\n- - b\n  - 2\n- - c\n  - 3\n').dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> FlatDict.from_yamls('- a: 1\n- b: 2\n- c: 3\n')
-        [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]
-    """
-
-    kwargs.setdefault("Loader", SafeLoader)
-    return cls.from_dict(yaml_load(string, *args, **kwargs))
+1302
+1303
@classmethod
+def from_yamls(cls, string: str, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Construct `FlatDict` from yaml string.
+
+    Returns:
+        (FlatDict):
+
+    Examples:
+        >>> FlatDict.from_yamls('a: 1\nb: 2\nc: 3\n').dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> FlatDict.from_yamls('- - a\n  - 1\n- - b\n  - 2\n- - c\n  - 3\n').dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> FlatDict.from_yamls('- a: 1\n- b: 2\n- c: 3\n')
+        [FlatDict(('a'): 1), FlatDict(('b'): 2), FlatDict(('c'): 3)]
+    """
+
+    kwargs.setdefault("Loader", SafeLoader)
+    return cls.from_dict(yaml_load(string, *args, **kwargs))
 
@@ -6675,7 +6677,8 @@

403 404 405 -406

def getattr(self, name: str, default: Any = Null) -> Any:
+406
+407
def getattr(self, name: str, default: Any = Null) -> Any:
     r"""
     Get attribute of `FlatDict`.
 
@@ -6708,13 +6711,14 @@ 

try: if name in self.__dict__: return self.__dict__[name] - if name in self.__class__.__dict__: - return self.__class__.__dict__[name] - return super().getattr(name, default) # type: ignore[misc] - except AttributeError: - if default is not Null: - return default - raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") from None + for cls in self.__class__.__mro__: + if name in cls.__dict__: + return cls.__dict__[name] + return super().getattr(name, default) # type: ignore[misc] + except AttributeError: + if default is not Null: + return default + raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") from None

@@ -6773,8 +6777,7 @@

Source code in chanfig/flat_dict.py -
Python
945
-946
+              
Python
946
 947
 948
 949
@@ -6793,27 +6796,28 @@ 

962 963 964 -965

def gpu(self) -> Self:  # pragma: no cover
-    r"""
-    Move all tensors to gpu.
-
-    Returns:
-        self:
-
-    **Alias**:
-
-    + `cuda`
-
-    Examples:
-        >>> import torch
-        >>> d = FlatDict(a=torch.tensor(1))
-        >>> d.gpu().dict()  # doctest: +SKIP
-        {'a': tensor(1, device='cuda:0')}
-        >>> d.cuda().dict()  # alias  # doctest: +SKIP
-        {'a': tensor(1, device='cuda:0')}
-    """
-
-    return self.to(TorchDevice("cuda"))
+965
+966
def gpu(self) -> Self:  # pragma: no cover
+    r"""
+    Move all tensors to gpu.
+
+    Returns:
+        self:
+
+    **Alias**:
+
+    + `cuda`
+
+    Examples:
+        >>> import torch
+        >>> d = FlatDict(a=torch.tensor(1))
+        >>> d.gpu().dict()  # doctest: +SKIP
+        {'a': tensor(1, device='cuda:0')}
+        >>> d.cuda().dict()  # alias  # doctest: +SKIP
+        {'a': tensor(1, device='cuda:0')}
+    """
+
+    return self.to(TorchDevice("cuda"))
 
@@ -6898,8 +6902,7 @@

Source code in chanfig/flat_dict.py -
Python
466
-467
+              
Python
467
 468
 469
 470
@@ -6923,32 +6926,33 @@ 

488 489 490 -491

def hasattr(self, name: str) -> bool:
-    r"""
-    Determine if an attribute exists in `FlatDict`.
-
-    Args:
-        name:
-
-    Returns:
-        (bool):
-
-    Examples:
-        >>> d = FlatDict()
-        >>> d.setattr('name', 'chang')
-        >>> d.hasattr('name')
-        True
-        >>> d.delattr('name')
-        >>> d.hasattr('name')
-        False
-    """
-
-    try:
-        if name in self.__dict__ or name in self.__class__.__dict__:
-            return True
-        return super().hasattr(name)  # type: ignore[misc]
-    except AttributeError:
-        return False
+491
+492
def hasattr(self, name: str) -> bool:
+    r"""
+    Determine if an attribute exists in `FlatDict`.
+
+    Args:
+        name:
+
+    Returns:
+        (bool):
+
+    Examples:
+        >>> d = FlatDict()
+        >>> d.setattr('name', 'chang')
+        >>> d.hasattr('name')
+        True
+        >>> d.delattr('name')
+        >>> d.hasattr('name')
+        False
+    """
+
+    try:
+        if name in self.__dict__ or name in self.__class__.__dict__:
+            return True
+        return super().hasattr(name)  # type: ignore[misc]
+    except AttributeError:
+        return False
 
@@ -6970,15 +6974,15 @@

Source code in chanfig/flat_dict.py -
Python
849
-850
+              
Python
def inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Alias of [`intersect`][chanfig.FlatDict.intersect].
-    """
-    return self.intersect(other, *args, **kwargs)
+853
+854
def inter(self, other: Mapping | Iterable | PathStr, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Alias of [`intersect`][chanfig.FlatDict.intersect].
+    """
+    return self.intersect(other, *args, **kwargs)
 
@@ -7155,8 +7159,7 @@

Source code in chanfig/flat_dict.py -
Python
590
-591
+              
Python
591
 592
 593
 594
@@ -7258,110 +7261,111 @@ 

690 691 692 -693

def interpolate(  # pylint: disable=R0912
-    self, use_variable: bool = True, interpolators: MutableMapping | None = None, unsafe_eval: bool = False
-) -> Self:
-    r"""
-    Perform Variable interpolation.
-
-    Variable interpolation allows you to set the value of one key to be the value of another key easily.
-
-    Args:
-        use_variable: Whether to convert values to `Variable` objects.
-        interpolators: Mapping contains values for interpolation. Defaults to `self`.
-        unsafe_eval: Whether to evaluate interpolated values.
-
-    Raises:
-        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:
-        >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}")
-        >>> d.dict()
-        {'a': 1, 'b': '${a}', 'c': '${a}.${b}'}
-        >>> d.interpolate(unsafe_eval=True).dict()
-        {'a': 1, 'b': 1, 'c': 1.1}
-        >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}")
-        >>> d.dict()
-        {'a': 1, 'b': '${a}', 'c': '${a}.${b}'}
-        >>> d.interpolate().dict()
-        {'a': 1, 'b': 1, 'c': '1.1'}
-        >>> isinstance(d.a, Variable)
-        True
-        >>> d.a += 1
-        >>> d.dict()
-        {'a': 2, 'b': 2, 'c': '1.1'}
-        >>> d.a is d.b
-        True
-        >>> d.b is d.c
-        False
-        >>> d = FlatDict(a=1, b="${a}", c="${b}")
-        >>> d.dict()
-        {'a': 1, 'b': '${a}', 'c': '${b}'}
-        >>> d.interpolate(False).dict()
-        {'a': 1, 'b': 1, 'c': 1}
-        >>> isinstance(d.a, Variable)
-        False
-        >>> d.a += 1
-        >>> d.dict()
-        {'a': 2, 'b': 1, 'c': 1}
-        >>> d = FlatDict(a=1, b="${b}", c="${b}")
-        >>> d.interpolate().dict()
-        Traceback (most recent call last):
-        ValueError: Cannot interpolate b to itself.
-        >>> d = FlatDict(a="${b}", b="${c}", c="${d}", d="${a}")
-        >>> d.interpolate().dict()
-        Traceback (most recent call last):
-        ValueError: Circular reference found: a->b->c->d->a.
-        >>> d = FlatDict(a=1, b="${a}", c="${d}")
-        >>> d.interpolate().dict()
-        Traceback (most recent call last):
-        ValueError: d is not found in FlatDict(
-          ('a'): '1'
-          ('b'): '${a}'
-          ('c'): '${d}'
-        ).
-    """
-    # pylint: disable=C0103
-
-    interpolators = interpolators or self
-    placeholders: dict[str, list[str]] = {}
-    for key, value in self.all_items():
-        if isinstance(value, list):
-            for v in value:
-                self.find_placeholders(key, v, placeholders)
-        elif isinstance(value, Mapping):
-            for v in value.values():
-                self.find_placeholders(key, v, placeholders)
-        else:
-            self.find_placeholders(key, value, placeholders)
-    circular_references = find_circular_reference(placeholders)
-    if circular_references:
-        raise ValueError(f"Circular reference found: {'->'.join(circular_references)}.")
-    if use_variable:
-        placeholder_names = {i for j in placeholders.values() for i in j}
-        for name in list(placeholder_names.difference(placeholders.keys())):
-            if name not in interpolators:
-                raise ValueError(f"{name} is not found in {interpolators}.")
-            if not isinstance(interpolators[name], Variable):
-                interpolators[name] = Variable(interpolators[name])
-    for key, value in placeholders.items():
-        if isinstance(self[key], list):
-            for index, v in enumerate(self[key]):
-                self[key][index] = self.substitute(v, interpolators, value)
-        elif isinstance(self[key], Mapping):
-            for k, v in self[key].items():
-                self[key][k] = self.substitute(v, interpolators, value)
-        else:
-            self[key] = self.substitute(self[key], interpolators, value)
-        if unsafe_eval and isinstance(self[key], str):
-            with suppress(SyntaxError):
-                self[key] = eval(self[key])  # pylint: disable=W0123
-    return self
+693
+694
def interpolate(  # pylint: disable=R0912
+    self, use_variable: bool = True, interpolators: MutableMapping | None = None, unsafe_eval: bool = False
+) -> Self:
+    r"""
+    Perform Variable interpolation.
+
+    Variable interpolation allows you to set the value of one key to be the value of another key easily.
+
+    Args:
+        use_variable: Whether to convert values to `Variable` objects.
+        interpolators: Mapping contains values for interpolation. Defaults to `self`.
+        unsafe_eval: Whether to evaluate interpolated values.
+
+    Raises:
+        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:
+        >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}")
+        >>> d.dict()
+        {'a': 1, 'b': '${a}', 'c': '${a}.${b}'}
+        >>> d.interpolate(unsafe_eval=True).dict()
+        {'a': 1, 'b': 1, 'c': 1.1}
+        >>> d = FlatDict(a=1, b="${a}", c="${a}.${b}")
+        >>> d.dict()
+        {'a': 1, 'b': '${a}', 'c': '${a}.${b}'}
+        >>> d.interpolate().dict()
+        {'a': 1, 'b': 1, 'c': '1.1'}
+        >>> isinstance(d.a, Variable)
+        True
+        >>> d.a += 1
+        >>> d.dict()
+        {'a': 2, 'b': 2, 'c': '1.1'}
+        >>> d.a is d.b
+        True
+        >>> d.b is d.c
+        False
+        >>> d = FlatDict(a=1, b="${a}", c="${b}")
+        >>> d.dict()
+        {'a': 1, 'b': '${a}', 'c': '${b}'}
+        >>> d.interpolate(False).dict()
+        {'a': 1, 'b': 1, 'c': 1}
+        >>> isinstance(d.a, Variable)
+        False
+        >>> d.a += 1
+        >>> d.dict()
+        {'a': 2, 'b': 1, 'c': 1}
+        >>> d = FlatDict(a=1, b="${b}", c="${b}")
+        >>> d.interpolate().dict()
+        Traceback (most recent call last):
+        ValueError: Cannot interpolate b to itself.
+        >>> d = FlatDict(a="${b}", b="${c}", c="${d}", d="${a}")
+        >>> d.interpolate().dict()
+        Traceback (most recent call last):
+        ValueError: Circular reference found: a->b->c->d->a.
+        >>> d = FlatDict(a=1, b="${a}", c="${d}")
+        >>> d.interpolate().dict()
+        Traceback (most recent call last):
+        ValueError: d is not found in FlatDict(
+          ('a'): '1'
+          ('b'): '${a}'
+          ('c'): '${d}'
+        ).
+    """
+    # pylint: disable=C0103
+
+    interpolators = interpolators or self
+    placeholders: dict[str, list[str]] = {}
+    for key, value in self.all_items():
+        if isinstance(value, list):
+            for v in value:
+                self.find_placeholders(key, v, placeholders)
+        elif isinstance(value, Mapping):
+            for v in value.values():
+                self.find_placeholders(key, v, placeholders)
+        else:
+            self.find_placeholders(key, value, placeholders)
+    circular_references = find_circular_reference(placeholders)
+    if circular_references:
+        raise ValueError(f"Circular reference found: {'->'.join(circular_references)}.")
+    if use_variable:
+        placeholder_names = {i for j in placeholders.values() for i in j}
+        for name in list(placeholder_names.difference(placeholders.keys())):
+            if name not in interpolators:
+                raise ValueError(f"{name} is not found in {interpolators}.")
+            if not isinstance(interpolators[name], Variable):
+                interpolators[name] = Variable(interpolators[name])
+    for key, value in placeholders.items():
+        if isinstance(self[key], list):
+            for index, v in enumerate(self[key]):
+                self[key][index] = self.substitute(v, interpolators, value)
+        elif isinstance(self[key], Mapping):
+            for k, v in self[key].items():
+                self[key][k] = self.substitute(v, interpolators, value)
+        else:
+            self[key] = self.substitute(self[key], interpolators, value)
+        if unsafe_eval and isinstance(self[key], str):
+            with suppress(SyntaxError):
+                self[key] = eval(self[key])  # pylint: disable=W0123
+    return self
 
@@ -7457,8 +7461,7 @@

Source code in chanfig/flat_dict.py -
Python
810
-811
+              
Python
811
 812
 813
 814
@@ -7494,44 +7497,45 @@ 

844 845 846 -847

def intersect(self, other: Mapping | Iterable | PathStr) -> Self:
-    r"""
-    Intersection of `FlatDict` and `other`.
-
-    Args:
-        other (Mapping | Iterable | PathStr):
-
-    Returns:
-        (FlatDict):
-
-    **Alias**:
-
-    + `inter`
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}
-        >>> d.intersect(n).dict()
-        {}
-        >>> l = [('c', 3), ('d', 4)]
-        >>> d.intersect(l).dict()
-        {'c': 3}
-        >>> d.merge(l).intersect("tests/test.yaml").dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> d.intersect(1)
-        Traceback (most recent call last):
-        TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.
-        >>> d.inter(FlatDict(b='b', c='c', d='d')).dict()  # alias
-        {}
-    """
-
-    if isinstance(other, (PathLike, str, bytes)):
-        other = self.load(other)
-    if isinstance(other, (Mapping,)):
-        other = self.empty(other).items()
-    if not isinstance(other, Iterable):
-        raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.")
-    return self.empty(**{key: value for key, value in other if key in self and self[key] == value})  # type: ignore
+847
+848
def intersect(self, other: Mapping | Iterable | PathStr) -> Self:
+    r"""
+    Intersection of `FlatDict` and `other`.
+
+    Args:
+        other (Mapping | Iterable | PathStr):
+
+    Returns:
+        (FlatDict):
+
+    **Alias**:
+
+    + `inter`
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}
+        >>> d.intersect(n).dict()
+        {}
+        >>> l = [('c', 3), ('d', 4)]
+        >>> d.intersect(l).dict()
+        {'c': 3}
+        >>> d.merge(l).intersect("tests/test.yaml").dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> d.intersect(1)
+        Traceback (most recent call last):
+        TypeError: `other=1` should be of type Mapping, Iterable or PathStr, but got <class 'int'>.
+        >>> d.inter(FlatDict(b='b', c='c', d='d')).dict()  # alias
+        {}
+    """
+
+    if isinstance(other, (PathLike, str, bytes)):
+        other = self.load(other)
+    if isinstance(other, (Mapping,)):
+        other = self.empty(other).items()
+    if not isinstance(other, Iterable):
+        raise TypeError(f"`other={other}` should be of type Mapping, Iterable or PathStr, but got {type(other)}.")
+    return self.empty(**{key: value for key, value in other if key in self and self[key] == value})  # type: ignore
 
@@ -7561,8 +7565,7 @@

Source code in chanfig/flat_dict.py -
Python
1158
-1159
+              
Python
1159
 1160
 1161
 1162
@@ -7574,20 +7577,21 @@ 

1168 1169 1170 -1171

def json(self, file: File, *args: Any, **kwargs: Any) -> None:
-    r"""
-    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:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.json("tests/test.json")
-    """
-
-    with self.open(file, mode="w") as fp:  # pylint: disable=C0103
-        fp.write(self.jsons(*args, **kwargs))
+1171
+1172
def json(self, file: File, *args: Any, **kwargs: Any) -> None:
+    r"""
+    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:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.json("tests/test.json")
+    """
+
+    with self.open(file, mode="w") as fp:  # pylint: disable=C0103
+        fp.write(self.jsons(*args, **kwargs))
 
@@ -7639,8 +7643,7 @@

Source code in chanfig/flat_dict.py -
Python
1195
-1196
+              
Python
1196
 1197
 1198
 1199
@@ -7654,22 +7657,23 @@ 

1207 1208 1209 -1210

def jsons(self, *args: Any, **kwargs: Any) -> str:
-    r"""
-    Dump `FlatDict` to json string.
-
-    Returns:
-        (str):
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.jsons()
-        '{\n  "a": 1,\n  "b": 2,\n  "c": 3\n}'
-    """
-
-    kwargs.setdefault("cls", JsonEncoder)
-    kwargs.setdefault("indent", self.getattr("indent", 2))
-    return json_dumps(self.dict(), *args, **kwargs)
+1210
+1211
def jsons(self, *args: Any, **kwargs: Any) -> str:
+    r"""
+    Dump `FlatDict` to json string.
+
+    Returns:
+        (str):
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.jsons()
+        '{\n  "a": 1,\n  "b": 2,\n  "c": 3\n}'
+    """
+
+    kwargs.setdefault("cls", JsonEncoder)
+    kwargs.setdefault("indent", self.getattr("indent", 2))
+    return json_dumps(self.dict(), *args, **kwargs)
 
@@ -7808,8 +7812,7 @@

Source code in chanfig/flat_dict.py -
Python
1116
-1117
+              
Python
1117
 1118
 1119
 1120
@@ -7848,47 +7851,48 @@ 

1153 1154 1155 -1156

@classmethod
-def load(  # pylint: disable=W1113
-    cls, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]
-) -> Self:
-    """
-    Load `FlatDict` from file.
-
-    Args:
-        file: File to load from.
-        method: File type, should be in `JSON` or `YAML`.
-
-    Returns:
-        (FlatDict):
-
-    Raises:
-        ValueError: If load from `IO` and `method` is not specified.
-        TypeError: If dump to unsupported extension.
-
-    Examples:
-        >>> d = FlatDict.load("tests/test.yaml")
-        >>> d.dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> d.load("tests/test.conf")
-        Traceback (most recent call last):
-        TypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.
-        >>> with open("tests/test.yaml") as f:
-        ...     d.load(f)
-        Traceback (most recent call last):
-        ValueError: `method` must be specified when loading from IO.
-    """
-
-    if method is None:
-        if isinstance(file, (IOBase, IO)):
-            raise ValueError("`method` must be specified when loading from IO.")
-        method = splitext(file)[-1][1:]
-    extension = method.lower()
-    if extension in JSON:
-        return cls.from_json(file, *args, **kwargs)
-    if extension in YAML:
-        return cls.from_yaml(file, *args, **kwargs)
-    raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.")
+1156
+1157
@classmethod
+def load(  # pylint: disable=W1113
+    cls, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]
+) -> Self:
+    """
+    Load `FlatDict` from file.
+
+    Args:
+        file: File to load from.
+        method: File type, should be in `JSON` or `YAML`.
+
+    Returns:
+        (FlatDict):
+
+    Raises:
+        ValueError: If load from `IO` and `method` is not specified.
+        TypeError: If dump to unsupported extension.
+
+    Examples:
+        >>> d = FlatDict.load("tests/test.yaml")
+        >>> d.dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> d.load("tests/test.conf")
+        Traceback (most recent call last):
+        TypeError: `file='tests/test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.
+        >>> with open("tests/test.yaml") as f:
+        ...     d.load(f)
+        Traceback (most recent call last):
+        ValueError: `method` must be specified when loading from IO.
+    """
+
+    if method is None:
+        if isinstance(file, (IOBase, IO)):
+            raise ValueError("`method` must be specified when loading from IO.")
+        method = splitext(file)[-1][1:]
+    extension = method.lower()
+    if extension in JSON:
+        return cls.from_json(file, *args, **kwargs)
+    if extension in YAML:
+        return cls.from_yaml(file, *args, **kwargs)
+    raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.")
 
@@ -8014,8 +8018,7 @@

Source code in chanfig/flat_dict.py -
Python
715
-716
+              
Python
716
 717
 718
 719
@@ -8062,55 +8065,56 @@ 

760 761 762 -763

def merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> Self:
-    r"""
-    Merge `other` into `FlatDict`.
-
-    Args:
-        *args: `Mapping` or `Sequence` to be merged.
-        overwrite: Whether to overwrite existing values.
-        **kwargs: `Mapping` to be merged.
-
-    Returns:
-        self:
-
-    **Alias**:
-
-    + `union`
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}
-        >>> d.merge(n).dict()
-        {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}
-        >>> l = [('c', 3), ('d', 4)]
-        >>> d.merge(l).dict()
-        {'a': 1, 'b': 'b', 'c': 3, 'd': 4}
-        >>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict()  # alias
-        {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}
-        >>> d = FlatDict()
-        >>> d.merge({1: 1, 2: 2, 3:3}).dict()
-        {1: 1, 2: 2, 3: 3}
-        >>> d.merge(d.clone()).dict()
-        {1: 1, 2: 2, 3: 3}
-        >>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict()
-        {1: 1, 2: 2, 3: 3, 4: 4, 5: 5}
-    """
-
-    if len(args) == 1:
-        args = args[0]
-        if isinstance(args, (PathLike, str, bytes)):
-            args = self.load(args)  # type: ignore[assignment]
-            warn(
-                "merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.",
-                PendingDeprecationWarning,
-            )
-        self._merge(self, args, overwrite=overwrite)
-    elif len(args) > 1:
-        self._merge(self, args, overwrite=overwrite)
-    if kwargs:
-        self._merge(self, kwargs, overwrite=overwrite)
-    return self
+763
+764
def merge(self, *args: Any, overwrite: bool = True, **kwargs: Any) -> Self:
+    r"""
+    Merge `other` into `FlatDict`.
+
+    Args:
+        *args: `Mapping` or `Sequence` to be merged.
+        overwrite: Whether to overwrite existing values.
+        **kwargs: `Mapping` to be merged.
+
+    Returns:
+        self:
+
+    **Alias**:
+
+    + `union`
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> n = {'b': 'b', 'c': 'c', 'd': 'd'}
+        >>> d.merge(n).dict()
+        {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}
+        >>> l = [('c', 3), ('d', 4)]
+        >>> d.merge(l).dict()
+        {'a': 1, 'b': 'b', 'c': 3, 'd': 4}
+        >>> FlatDict(a=1, b=1, c=1).union(FlatDict(b='b', c='c', d='d')).dict()  # alias
+        {'a': 1, 'b': 'b', 'c': 'c', 'd': 'd'}
+        >>> d = FlatDict()
+        >>> d.merge({1: 1, 2: 2, 3:3}).dict()
+        {1: 1, 2: 2, 3: 3}
+        >>> d.merge(d.clone()).dict()
+        {1: 1, 2: 2, 3: 3}
+        >>> d.merge({1:3, 2:1, 3: 2, 4: 4, 5: 5}, overwrite=False).dict()
+        {1: 1, 2: 2, 3: 3, 4: 4, 5: 5}
+    """
+
+    if len(args) == 1:
+        args = args[0]
+        if isinstance(args, (PathLike, str, bytes)):
+            args = self.load(args)  # type: ignore[assignment]
+            warn(
+                "merge file is deprecated and maybe removed in a future release. Use `merge_from_file` instead.",
+                PendingDeprecationWarning,
+            )
+        self._merge(self, args, overwrite=overwrite)
+    elif len(args) > 1:
+        self._merge(self, args, overwrite=overwrite)
+    if kwargs:
+        self._merge(self, kwargs, overwrite=overwrite)
+    return self
 
@@ -8219,8 +8223,7 @@

Source code in chanfig/flat_dict.py -
Python
790
-791
+              
Python
791
 792
 793
 794
@@ -8237,25 +8240,26 @@ 

805 806 807 -808

def merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Merge content of `file` into `FlatDict`.
-
-    Args:
-        file (File):
-        *args: Passed to [`load`][chanfig.FlatDict.load].
-        **kwargs: Passed to [`load`][chanfig.FlatDict.load].
-
-    Returns:
-        self:
-
-    Examples:
-        >>> d = FlatDict(a=1, b=1)
-        >>> d.merge_from_file("tests/test.yaml").dict()
-        {'a': 1, 'b': 2, 'c': 3}
-    """
-
-    return self.merge(self.load(file, *args, **kwargs))
+808
+809
def merge_from_file(self, file: File, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Merge content of `file` into `FlatDict`.
+
+    Args:
+        file (File):
+        *args: Passed to [`load`][chanfig.FlatDict.load].
+        **kwargs: Passed to [`load`][chanfig.FlatDict.load].
+
+    Returns:
+        self:
+
+    Examples:
+        >>> d = FlatDict(a=1, b=1)
+        >>> d.merge_from_file("tests/test.yaml").dict()
+        {'a': 1, 'b': 2, 'c': 3}
+    """
+
+    return self.merge(self.load(file, *args, **kwargs))
 
@@ -8386,8 +8390,7 @@

Source code in chanfig/flat_dict.py -
Python
1304
-1305
+              
Python
1305
 1306
 1307
 1308
@@ -8435,56 +8438,57 @@ 

1350 1351 1352 -1353

@staticmethod
-@contextmanager
-def open(file: File, *args: Any, encoding: str = "utf-8", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]:
-    r"""
-    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.
-
-    Args:
-        file: File path or IO.
-        *args: Additional arguments passed to `open`.
-            Defaults to ().
-        **kwargs: Any
-            Additional keyword arguments passed to `open`.
-            Defaults to {}.
-
-    Yields:
-        (Generator[IOBase | IO, Any, Any]):
-
-    Examples:
-        >>> with FlatDict.open("tests/test.yaml") as fp:
-        ...     print(fp.read())
-        a: 1
-        b: 2
-        c: 3
-        <BLANKLINE>
-        >>> io = open("tests/test.yaml")
-        >>> with FlatDict.open(io) as fp:
-        ...     print(fp.read())
-        a: 1
-        b: 2
-        c: 3
-        <BLANKLINE>
-        >>> with FlatDict.open(123, mode="w") as fp:
-        ...     print(fp.read())
-        Traceback (most recent call last):
-        TypeError: expected str, bytes, os.PathLike, IO or IOBase, not int
-    """
-
-    if isinstance(file, (IOBase, IO)):
-        yield file
-    elif isinstance(file, (PathLike, str, bytes)):
-        try:
-            file = open(file, *args, encoding=encoding, **kwargs)  # type: ignore[call-overload] # noqa: SIM115
-            yield file  # type: ignore[misc]
-        finally:
-            with suppress(Exception):
-                file.close()  # type: ignore[union-attr]
-    else:
-        raise TypeError(f"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}")
+1353
+1354
@staticmethod
+@contextmanager
+def open(file: File, *args: Any, encoding: str = "utf-8", **kwargs: Any) -> Generator[IOBase | IO, Any, Any]:
+    r"""
+    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.
+
+    Args:
+        file: File path or IO.
+        *args: Additional arguments passed to `open`.
+            Defaults to ().
+        **kwargs: Any
+            Additional keyword arguments passed to `open`.
+            Defaults to {}.
+
+    Yields:
+        (Generator[IOBase | IO, Any, Any]):
+
+    Examples:
+        >>> with FlatDict.open("tests/test.yaml") as fp:
+        ...     print(fp.read())
+        a: 1
+        b: 2
+        c: 3
+        <BLANKLINE>
+        >>> io = open("tests/test.yaml")
+        >>> with FlatDict.open(io) as fp:
+        ...     print(fp.read())
+        a: 1
+        b: 2
+        c: 3
+        <BLANKLINE>
+        >>> with FlatDict.open(123, mode="w") as fp:
+        ...     print(fp.read())
+        Traceback (most recent call last):
+        TypeError: expected str, bytes, os.PathLike, IO or IOBase, not int
+    """
+
+    if isinstance(file, (IOBase, IO)):
+        yield file
+    elif isinstance(file, (PathLike, str, bytes)):
+        try:
+            file = open(file, *args, encoding=encoding, **kwargs)  # type: ignore[call-overload] # noqa: SIM115
+            yield file  # type: ignore[misc]
+        finally:
+            with suppress(Exception):
+                file.close()  # type: ignore[union-attr]
+    else:
+        raise TypeError(f"expected str, bytes, os.PathLike, IO or IOBase, not {type(file).__name__}")
 
@@ -8556,8 +8560,7 @@

Source code in chanfig/flat_dict.py -
Python
1071
-1072
+              
Python
1072
 1073
 1074
 1075
@@ -8591,42 +8594,43 @@ 

1103 1104 1105 -1106

def save(  # pylint: disable=W1113
-    self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]
-) -> None:
-    r"""
-    Save `FlatDict` to file.
-
-    Raises:
-        ValueError: If save to `IO` and `method` is not specified.
-        TypeError: If save to unsupported extension.
-
-    **Alias**:
-
-    + `save`
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.save("tests/test.yaml")
-        >>> d.save("test.conf")
-        Traceback (most recent call last):
-        TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.
-        >>> with open("test.yaml", "w") as f:
-        ...     d.save(f)
-        Traceback (most recent call last):
-        ValueError: `method` must be specified when saving to IO.
-    """
-
-    if method is None:
-        if isinstance(file, (IOBase, IO)):
-            raise ValueError("`method` must be specified when saving to IO.")
-        method = splitext(file)[-1][1:]
-    extension = method.lower()
-    if extension in YAML:
-        return self.yaml(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026
-    if extension in JSON:
-        return self.json(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026
-    raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.")
+1106
+1107
def save(  # pylint: disable=W1113
+    self, file: File, method: str = None, *args: Any, **kwargs: Any  # type: ignore[assignment]
+) -> None:
+    r"""
+    Save `FlatDict` to file.
+
+    Raises:
+        ValueError: If save to `IO` and `method` is not specified.
+        TypeError: If save to unsupported extension.
+
+    **Alias**:
+
+    + `save`
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.save("tests/test.yaml")
+        >>> d.save("test.conf")
+        Traceback (most recent call last):
+        TypeError: `file='test.conf'` should be in ('json',) or ('yml', 'yaml'), but got conf.
+        >>> with open("test.yaml", "w") as f:
+        ...     d.save(f)
+        Traceback (most recent call last):
+        ValueError: `method` must be specified when saving to IO.
+    """
+
+    if method is None:
+        if isinstance(file, (IOBase, IO)):
+            raise ValueError("`method` must be specified when saving to IO.")
+        method = splitext(file)[-1][1:]
+    extension = method.lower()
+    if extension in YAML:
+        return self.yaml(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026
+    if extension in JSON:
+        return self.json(file=file, *args, **kwargs)  # type: ignore[misc]  # noqa: B026
+    raise TypeError(f"`file={file!r}` should be in {JSON} or {YAML}, but got {extension}.")
 
@@ -8862,8 +8866,7 @@

Source code in chanfig/flat_dict.py -
Python
408
-409
+              
Python
409
 410
 411
 412
@@ -8896,41 +8899,42 @@ 

439 440 441 -442

def setattr(self, name: str, value: Any) -> None:
-    r"""
-    Set attribute of `FlatDict`.
-
-    Note that it won't alter values in `FlatDict`.
-
-    Args:
-        name:
-        value:
-
-    Warns:
-        RuntimeWarning: If name already exists in `FlatDict`.
-
-    Examples:
-        >>> d = FlatDict()
-        >>> d.setattr('attr', 'value')
-        >>> d.getattr('attr')
-        'value'
-        >>> d.set('d', 1013)
-        >>> d.setattr('d', 1031)  # RuntimeWarning: d already exists in FlatDict.
-        >>> d.get('d')
-        1013
-        >>> d.d
-        1013
-        >>> d.getattr('d')
-        1031
-    """
-
-    if name in self:
-        warn(
-            f"{name} already exists in {self.__class__.__name__}.\n"
-            f"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.",
-            RuntimeWarning,
-        )
-    self.__dict__[name] = value
+442
+443
def setattr(self, name: str, value: Any) -> None:
+    r"""
+    Set attribute of `FlatDict`.
+
+    Note that it won't alter values in `FlatDict`.
+
+    Args:
+        name:
+        value:
+
+    Warns:
+        RuntimeWarning: If name already exists in `FlatDict`.
+
+    Examples:
+        >>> d = FlatDict()
+        >>> d.setattr('attr', 'value')
+        >>> d.getattr('attr')
+        'value'
+        >>> d.set('d', 1013)
+        >>> d.setattr('d', 1031)  # RuntimeWarning: d already exists in FlatDict.
+        >>> d.get('d')
+        1013
+        >>> d.d
+        1013
+        >>> d.getattr('d')
+        1031
+    """
+
+    if name in self:
+        warn(
+            f"{name} already exists in {self.__class__.__name__}.\n"
+            f"Users must call `{self.__class__.__name__}.getattr()` to retrieve conflicting attribute value.",
+            RuntimeWarning,
+        )
+    self.__dict__[name] = value
 
@@ -8990,8 +8994,7 @@

Source code in chanfig/flat_dict.py -
Python
563
-564
+              
Python
564
 565
 566
 567
@@ -9015,32 +9018,33 @@ 

585 586 587 -588

def sort(self, key: Callable | None = None, reverse: bool = False) -> Self:
-    r"""
-    Sort `FlatDict`.
-
-    Returns:
-        (FlatDict):
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.sort().dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> d = FlatDict(b=2, c=3, a=1)
-        >>> d.sort().dict()
-        {'a': 1, 'b': 2, 'c': 3}
-        >>> a = [1]
-        >>> d = FlatDict(z=0, a=a)
-        >>> a.append(2)
-        >>> d.sort().dict()
-        {'a': [1, 2], 'z': 0}
-    """
-
-    items = sorted(self.items(), key=key, reverse=reverse)
-    self.clear()
-    for k, v in items:  # pylint: disable=C0103
-        self[k] = v
-    return self
+588
+589
def sort(self, key: Callable | None = None, reverse: bool = False) -> Self:
+    r"""
+    Sort `FlatDict`.
+
+    Returns:
+        (FlatDict):
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.sort().dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> d = FlatDict(b=2, c=3, a=1)
+        >>> d.sort().dict()
+        {'a': 1, 'b': 2, 'c': 3}
+        >>> a = [1]
+        >>> d = FlatDict(z=0, a=a)
+        >>> a.append(2)
+        >>> d.sort().dict()
+        {'a': [1, 2], 'z': 0}
+    """
+
+    items = sorted(self.items(), key=key, reverse=reverse)
+    self.clear()
+    for k, v in items:  # pylint: disable=C0103
+        self[k] = v
+    return self
 
@@ -9122,8 +9126,7 @@

Source code in chanfig/flat_dict.py -
Python
902
-903
+              
Python
903
 904
 905
 906
@@ -9147,32 +9150,33 @@ 

924 925 926 -927

def to(self, cls: str | TorchDevice | TorchDType) -> Self:  # pragma: no cover
-    r"""
-    Convert values of `FlatDict` to target `cls`.
-
-    Args:
-        cls (str | torch.device | torch.dtype):
-
-    Returns:
-        self:
-
-    Examples:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.to(int)
-        Traceback (most recent call last):
-        TypeError: to() only support torch.dtype and torch.device, but got <class 'int'>.
-    """
-
-    # pylint: disable=C0103
-
-    if isinstance(cls, (str, TorchDevice, TorchDType)):
-        for k, v in self.all_items():
-            if hasattr(v, "to"):
-                self[k] = v.to(cls)
-        return self
-
-    raise TypeError(f"to() only support torch.dtype and torch.device, but got {cls}.")
+927
+928
def to(self, cls: str | TorchDevice | TorchDType) -> Self:  # pragma: no cover
+    r"""
+    Convert values of `FlatDict` to target `cls`.
+
+    Args:
+        cls (str | torch.device | torch.dtype):
+
+    Returns:
+        self:
+
+    Examples:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.to(int)
+        Traceback (most recent call last):
+        TypeError: to() only support torch.dtype and torch.device, but got <class 'int'>.
+    """
+
+    # pylint: disable=C0103
+
+    if isinstance(cls, (str, TorchDevice, TorchDType)):
+        for k, v in self.all_items():
+            if hasattr(v, "to"):
+                self[k] = v.to(cls)
+        return self
+
+    raise TypeError(f"to() only support torch.dtype and torch.device, but got {cls}.")
 
@@ -9194,17 +9198,17 @@

Source code in chanfig/flat_dict.py -
Python
518
-519
+              
Python
def to_dict(self, flatten: bool = False) -> Mapping | Sequence | Set:
-    r"""
-    Alias of [`dict`][chanfig.FlatDict.dict].
-    """
-
-    return self.dict(flatten)
+523
+524
def to_dict(self, flatten: bool = False) -> Mapping | Sequence | Set:
+    r"""
+    Alias of [`dict`][chanfig.FlatDict.dict].
+    """
+
+    return self.dict(flatten)
 
@@ -9263,8 +9267,7 @@

Source code in chanfig/flat_dict.py -
Python
973
-974
+              
Python
974
 975
 976
 977
@@ -9283,27 +9286,28 @@ 

990 991 992 -993

def tpu(self) -> Self:  # pragma: no cover
-    r"""
-    Move all tensors to tpu.
-
-    Returns:
-        self:
-
-    **Alias**:
-
-    + `xla`
-
-    Examples:
-        >>> import torch
-        >>> d = FlatDict(a=torch.tensor(1))
-        >>> d.tpu().dict()  # doctest: +SKIP
-        {'a': tensor(1, device='xla:0')}
-        >>> d.xla().dict()  # alias  # doctest: +SKIP
-        {'a': tensor(1, device='xla:0')}
-    """
-
-    return self.to(TorchDevice("xla"))
+993
+994
def tpu(self) -> Self:  # pragma: no cover
+    r"""
+    Move all tensors to tpu.
+
+    Returns:
+        self:
+
+    **Alias**:
+
+    + `xla`
+
+    Examples:
+        >>> import torch
+        >>> d = FlatDict(a=torch.tensor(1))
+        >>> d.tpu().dict()  # doctest: +SKIP
+        {'a': tensor(1, device='xla:0')}
+        >>> d.xla().dict()  # alias  # doctest: +SKIP
+        {'a': tensor(1, device='xla:0')}
+    """
+
+    return self.to(TorchDevice("xla"))
 
@@ -9325,15 +9329,15 @@

Source code in chanfig/flat_dict.py -
Python
784
-785
+              
Python
def union(self, *args: Any, **kwargs: Any) -> Self:
-    r"""
-    Alias of [`merge`][chanfig.FlatDict.merge].
-    """
-    return self.merge(*args, **kwargs)
+788
+789
def union(self, *args: Any, **kwargs: Any) -> Self:
+    r"""
+    Alias of [`merge`][chanfig.FlatDict.merge].
+    """
+    return self.merge(*args, **kwargs)
 
@@ -9469,15 +9473,15 @@

Source code in chanfig/flat_dict.py -
Python
def xla(self) -> Self:  # pragma: no cover
-    r"""
-    Alias of [`tpu`][chanfig.FlatDict.tpu].
-    """
-    return self.tpu()
+              
Python
def xla(self) -> Self:  # pragma: no cover
+    r"""
+    Alias of [`tpu`][chanfig.FlatDict.tpu].
+    """
+    return self.tpu()
 
@@ -9507,8 +9511,7 @@

Source code in chanfig/flat_dict.py -
Python
1231
-1232
+              
Python
1232
 1233
 1234
 1235
@@ -9520,20 +9523,21 @@ 

1241 1242 1243 -1244

def yaml(self, file: File, *args: Any, **kwargs: Any) -> None:
-    r"""
-    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:
-        >>> d = FlatDict(a=1, b=2, c=3)
-        >>> d.yaml("tests/test.yaml")
-    """
-
-    with self.open(file, mode="w") as fp:  # pylint: disable=C0103
-        self.yamls(fp, *args, **kwargs)
+1244
+1245
def yaml(self, file: File, *args: Any, **kwargs: Any) -> None:
+    r"""
+    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:
+        >>> d = FlatDict(a=1, b=2, c=3)
+        >>> d.yaml("tests/test.yaml")
+    """
+
+    with self.open(file, mode="w") as fp:  # pylint: disable=C0103
+        self.yamls(fp, *args, **kwargs)
 
@@ -9584,8 +9588,7 @@

Source code in chanfig/flat_dict.py -
Python
1268
-1269
+              
Python
1269
 1270
 1271
 1272
@@ -9598,21 +9601,22 @@ 

1279 1280 1281 -1282

def yamls(self, *args: Any, **kwargs: Any) -> str:
-    r"""
-    Dump `FlatDict` to yaml string.
-
-    Returns:
-        (str):
-
-    Examples:
-        >>> FlatDict(a=1, b=2, c=3).yamls()
-        'a: 1\nb: 2\nc: 3\n'
-    """
-
-    kwargs.setdefault("Dumper", YamlDumper)
-    kwargs.setdefault("indent", self.getattr("indent", 2))
-    return yaml_dump(self.dict(), *args, **kwargs)
+1282
+1283
def yamls(self, *args: Any, **kwargs: Any) -> str:
+    r"""
+    Dump `FlatDict` to yaml string.
+
+    Returns:
+        (str):
+
+    Examples:
+        >>> FlatDict(a=1, b=2, c=3).yamls()
+        'a: 1\nb: 2\nc: 3\n'
+    """
+
+    kwargs.setdefault("Dumper", YamlDumper)
+    kwargs.setdefault("indent", self.getattr("indent", 2))
+    return yaml_dump(self.dict(), *args, **kwargs)
 
diff --git a/zh/nested_dict/index.html b/zh/nested_dict/index.html index ba70d1ac..1c6fb42b 100644 --- a/zh/nested_dict/index.html +++ b/zh/nested_dict/index.html @@ -2286,7 +2286,7 @@

NestedDict delimiter = self.getattr("delimiter", ".") if convert_mapping is None: convert_mapping = self.getattr("convert_mapping", False) - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty try: while isinstance(name, str) and delimiter in name: name, rest = name.split(delimiter, 1) @@ -2300,7 +2300,7 @@

NestedDict else: self, name = self[name], rest if isinstance(self, NestedDict): - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty except (AttributeError, TypeError): raise KeyError(name) from None @@ -2453,7 +2453,7 @@

NestedDict delimiter = self.getattr("delimiter", ".") if convert_mapping is None: convert_mapping = self.getattr("convert_mapping", False) - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty try: while isinstance(name, str) and delimiter in name: name, rest = name.split(delimiter, 1) @@ -2467,7 +2467,7 @@

NestedDict else: self, name = self[name], rest if isinstance(self, NestedDict): - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty except (AttributeError, TypeError): raise KeyError(name) from None @@ -4481,7 +4481,7 @@

delimiter = self.getattr("delimiter", ".") if convert_mapping is None: convert_mapping = self.getattr("convert_mapping", False) - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty try: while isinstance(name, str) and delimiter in name: name, rest = name.split(delimiter, 1) @@ -4495,7 +4495,7 @@

else: self, name = self[name], rest if isinstance(self, NestedDict): - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty except (AttributeError, TypeError): raise KeyError(name) from None @@ -4749,7 +4749,7 @@

delimiter = self.getattr("delimiter", ".") if convert_mapping is None: convert_mapping = self.getattr("convert_mapping", False) - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty try: while isinstance(name, str) and delimiter in name: name, rest = name.split(delimiter, 1) @@ -4763,7 +4763,7 @@

else: self, name = self[name], rest if isinstance(self, NestedDict): - default_factory = self.getattr("default_factory", self.empty) + default_factory = self.getattr("default_factory", self.empty) or self.empty except (AttributeError, TypeError): raise KeyError(name) from None diff --git a/zh/registry/index.html b/zh/registry/index.html index 03991e18..ea32afad 100644 --- a/zh/registry/index.html +++ b/zh/registry/index.html @@ -1161,7 +1161,13 @@

Source code in chanfig/registry.py -
Python
286
+                
Python
280
+281
+282
+283
+284
+285
+286
 287
 288
 289
@@ -1300,158 +1306,152 @@ 

422 423 424 -425 -426 -427 -428 -429 -430 -431

class ConfigRegistry(Registry):
-    """
-    `ConfigRegistry` for components that can be initialised with a `config`.
+425
class ConfigRegistry(Registry):
+    """
+    `ConfigRegistry` for components that can be initialised with a `config`.
+
+    `ConfigRegistry` is particularly useful when you want to construct a component from a configuration, such as a
+    Hugginface Transformers model.
+
+    See Also:
+        [`Registry`][chanfig.Registry]: General purpose Registry.
 
-    `ConfigRegistry` is particularly useful when you want to construct a component from a configuration, such as a
-    Hugginface Transformers model.
-
-    See Also:
-        [`Registry`][chanfig.Registry]: General purpose Registry.
-
-    Examples:
-        >>> from dataclasses import dataclass, field
-        >>> @dataclass
-        ... class Config:
-        ...     a: int
-        ...     b: int
-        ...     mode: str = "proj"
-        >>> registry = ConfigRegistry(key="mode")
-        >>> @registry.register("proj")
-        ... class Proj:
-        ...     def __init__(self, config):
-        ...         self.a = config.a
-        ...         self.b = config.b
-        >>> @registry.register("inv")
-        ... class Inv:
-        ...     def __init__(self, config):
-        ...         self.a = config.b
-        ...         self.b = config.a
-        >>> registry
-        ConfigRegistry(
-          ('proj'): <class 'chanfig.registry.Proj'>
-          ('inv'): <class 'chanfig.registry.Inv'>
-        )
-        >>> config = Config(a=0, b=1)
-        >>> module = registry.build(config)
-        >>> module.a, module.b
-        (0, 1)
-        >>> config = Config(a=0, b=1, mode="inv")
-        >>> module = registry.build(config)
-        >>> module.a, module.b
-        (1, 0)
-        >>> @dataclass
-        ... class ModuleConfig:
-        ...     a: int = 0
-        ...     b: int = 1
-        ...     mode: str = "proj"
-        >>> @dataclass
-        ... class NestedConfig:
-        ...     module: ModuleConfig = field(default_factory=ModuleConfig)
-        >>> nested_registry = ConfigRegistry(key="module.mode")
-        >>> @nested_registry.register("proj")
-        ... class Proj:
-        ...     def __init__(self, config):
-        ...         self.a = config.module.a
-        ...         self.b = config.module.b
-        >>> @nested_registry.register("inv")
-        ... class Inv:
-        ...     def __init__(self, config):
-        ...         self.a = config.module.b
-        ...         self.b = config.module.a
-        >>> nested_config = NestedConfig()
-        >>> module = nested_registry.build(nested_config)
-        >>> module.a, module.b
-        (0, 1)
-    """
-
-    @staticmethod
-    def init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
-        r"""
-        Constructor of component.
-
-        Args:
-            cls: The component to construct.
-            *args: The arguments to pass to the component.
-            **kwargs: The keyword arguments to pass to the component.
-
-        Returns:
-            (Any):
-
-        Examples:
-            >>> class Module:
-            ...     def __init__(self, config, a=None, b=None):
-            ...         self.config = config
-            ...         self.a = config.a if a is None else a
-            ...         self.b = config.b if b is None else b
-            >>> config = NestedDict({"a": 0, "b": 1})
-            >>> module = ConfigRegistry.init(Module, config, b=2)
-            >>> module.a, module.b
-            (0, 2)
-        """
+    Examples:
+        >>> from dataclasses import dataclass, field
+        >>> @dataclass
+        ... class Config:
+        ...     a: int
+        ...     b: int
+        ...     mode: str = "proj"
+        >>> registry = ConfigRegistry(key="mode")
+        >>> @registry.register("proj")
+        ... class Proj:
+        ...     def __init__(self, config):
+        ...         self.a = config.a
+        ...         self.b = config.b
+        >>> @registry.register("inv")
+        ... class Inv:
+        ...     def __init__(self, config):
+        ...         self.a = config.b
+        ...         self.b = config.a
+        >>> registry
+        ConfigRegistry(
+          ('proj'): <class 'chanfig.registry.Proj'>
+          ('inv'): <class 'chanfig.registry.Inv'>
+        )
+        >>> config = Config(a=0, b=1)
+        >>> module = registry.build(config)
+        >>> module.a, module.b
+        (0, 1)
+        >>> config = Config(a=0, b=1, mode="inv")
+        >>> module = registry.build(config)
+        >>> module.a, module.b
+        (1, 0)
+        >>> @dataclass
+        ... class ModuleConfig:
+        ...     a: int = 0
+        ...     b: int = 1
+        ...     mode: str = "proj"
+        >>> @dataclass
+        ... class NestedConfig:
+        ...     module: ModuleConfig = field(default_factory=ModuleConfig)
+        >>> nested_registry = ConfigRegistry(key="module.mode")
+        >>> @nested_registry.register("proj")
+        ... class Proj:
+        ...     def __init__(self, config):
+        ...         self.a = config.module.a
+        ...         self.b = config.module.b
+        >>> @nested_registry.register("inv")
+        ... class Inv:
+        ...     def __init__(self, config):
+        ...         self.a = config.module.b
+        ...         self.b = config.module.a
+        >>> nested_config = NestedConfig()
+        >>> module = nested_registry.build(nested_config)
+        >>> module.a, module.b
+        (0, 1)
+    """
+
+    @staticmethod
+    def init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
+        r"""
+        Constructor of component.
+
+        Args:
+            cls: The component to construct.
+            *args: The arguments to pass to the component.
+            **kwargs: The keyword arguments to pass to the component.
+
+        Returns:
+            (Any):
+
+        Examples:
+            >>> class Module:
+            ...     def __init__(self, config, a=None, b=None):
+            ...         self.config = config
+            ...         self.a = config.a if a is None else a
+            ...         self.b = config.b if b is None else b
+            >>> config = NestedDict({"a": 0, "b": 1})
+            >>> module = ConfigRegistry.init(Module, config, b=2)
+            >>> module.a, module.b
+            (0, 2)
+        """
+
+        return cls(config, *args, **kwargs)
+
+    def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]
+        r"""
+        Build a component.
 
-        return cls(config, *args, **kwargs)
-
-    def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]
-        r"""
-        Build a component.
+        Args:
+            config
+
+        Returns:
+            (Any):
 
-        Args:
-            config
+        Raises:
+            KeyError: If the component is not registered.
 
-        Returns:
-            (Any):
-
-        Raises:
-            KeyError: If the component is not registered.
-
-        Examples:
-            >>> from dataclasses import dataclass, field
-            >>> registry = ConfigRegistry(key="module.mode")
-            >>> @registry.register("proj")
-            ... class Proj:
-            ...     def __init__(self, config):
-            ...         self.a = config.module.a
-            ...         self.b = config.module.b
-            >>> @registry.register("inv")
-            ... class Inv:
-            ...     def __init__(self, config):
-            ...         self.a = config.module.b
-            ...         self.b = config.module.a
-            >>> @dataclass
-            ... class ModuleConfig:
-            ...     a: int = 0
-            ...     b: int = 1
-            ...     mode: str = "proj"
-            >>> @dataclass
-            ... class Config:
-            ...     module: ModuleConfig = field(default_factory=ModuleConfig)
-            >>> config = Config()
-            >>> module = registry.build(config)
-            >>> type(module)
-            <class 'chanfig.registry.Proj'>
-            >>> module.a, module.b
-            (0, 1)
-            >>> type(module)
-            <class 'chanfig.registry.Proj'>
-        """
-
-        key = self.getattr("key")
-        config_ = deepcopy(config)
-
-        while "." in key:
-            key, rest = key.split(".", 1)
-            config_, key = getattr(config_, key), rest
-        name = getattr(config_, key)
-
-        return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]
+        Examples:
+            >>> from dataclasses import dataclass, field
+            >>> registry = ConfigRegistry(key="module.mode")
+            >>> @registry.register("proj")
+            ... class Proj:
+            ...     def __init__(self, config):
+            ...         self.a = config.module.a
+            ...         self.b = config.module.b
+            >>> @registry.register("inv")
+            ... class Inv:
+            ...     def __init__(self, config):
+            ...         self.a = config.module.b
+            ...         self.b = config.module.a
+            >>> @dataclass
+            ... class ModuleConfig:
+            ...     a: int = 0
+            ...     b: int = 1
+            ...     mode: str = "proj"
+            >>> @dataclass
+            ... class Config:
+            ...     module: ModuleConfig = field(default_factory=ModuleConfig)
+            >>> config = Config()
+            >>> module = registry.build(config)
+            >>> type(module)
+            <class 'chanfig.registry.Proj'>
+            >>> module.a, module.b
+            (0, 1)
+            >>> type(module)
+            <class 'chanfig.registry.Proj'>
+        """
+
+        key = self.getattr("key")
+        config_ = deepcopy(config)
+
+        while "." in key:
+            key, rest = key.split(".", 1)
+            config_, key = getattr(config_, key), rest
+        name = getattr(config_, key)
+
+        return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]
 
@@ -1560,7 +1560,13 @@

Source code in chanfig/registry.py -
Python
379
+              
Python
373
+374
+375
+376
+377
+378
+379
 380
 381
 382
@@ -1606,65 +1612,59 @@ 

422 423 424 -425 -426 -427 -428 -429 -430 -431

def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]
-    r"""
-    Build a component.
+425
def build(self, config, *args, **kwargs) -> Any:  # type: ignore[override]
+    r"""
+    Build a component.
+
+    Args:
+        config
+
+    Returns:
+        (Any):
 
-    Args:
-        config
+    Raises:
+        KeyError: If the component is not registered.
 
-    Returns:
-        (Any):
-
-    Raises:
-        KeyError: If the component is not registered.
-
-    Examples:
-        >>> from dataclasses import dataclass, field
-        >>> registry = ConfigRegistry(key="module.mode")
-        >>> @registry.register("proj")
-        ... class Proj:
-        ...     def __init__(self, config):
-        ...         self.a = config.module.a
-        ...         self.b = config.module.b
-        >>> @registry.register("inv")
-        ... class Inv:
-        ...     def __init__(self, config):
-        ...         self.a = config.module.b
-        ...         self.b = config.module.a
-        >>> @dataclass
-        ... class ModuleConfig:
-        ...     a: int = 0
-        ...     b: int = 1
-        ...     mode: str = "proj"
-        >>> @dataclass
-        ... class Config:
-        ...     module: ModuleConfig = field(default_factory=ModuleConfig)
-        >>> config = Config()
-        >>> module = registry.build(config)
-        >>> type(module)
-        <class 'chanfig.registry.Proj'>
-        >>> module.a, module.b
-        (0, 1)
-        >>> type(module)
-        <class 'chanfig.registry.Proj'>
-    """
-
-    key = self.getattr("key")
-    config_ = deepcopy(config)
-
-    while "." in key:
-        key, rest = key.split(".", 1)
-        config_, key = getattr(config_, key), rest
-    name = getattr(config_, key)
-
-    return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]
+    Examples:
+        >>> from dataclasses import dataclass, field
+        >>> registry = ConfigRegistry(key="module.mode")
+        >>> @registry.register("proj")
+        ... class Proj:
+        ...     def __init__(self, config):
+        ...         self.a = config.module.a
+        ...         self.b = config.module.b
+        >>> @registry.register("inv")
+        ... class Inv:
+        ...     def __init__(self, config):
+        ...         self.a = config.module.b
+        ...         self.b = config.module.a
+        >>> @dataclass
+        ... class ModuleConfig:
+        ...     a: int = 0
+        ...     b: int = 1
+        ...     mode: str = "proj"
+        >>> @dataclass
+        ... class Config:
+        ...     module: ModuleConfig = field(default_factory=ModuleConfig)
+        >>> config = Config()
+        >>> module = registry.build(config)
+        >>> type(module)
+        <class 'chanfig.registry.Proj'>
+        >>> module.a, module.b
+        (0, 1)
+        >>> type(module)
+        <class 'chanfig.registry.Proj'>
+    """
+
+    key = self.getattr("key")
+    config_ = deepcopy(config)
+
+    while "." in key:
+        key, rest = key.split(".", 1)
+        config_, key = getattr(config_, key), rest
+    name = getattr(config_, key)
+
+    return self.init(self.lookup(name), config, *args, **kwargs)  # type: ignore[arg-type]
 
@@ -1783,7 +1783,13 @@

Source code in chanfig/registry.py -
Python
352
+              
Python
346
+347
+348
+349
+350
+351
+352
 353
 354
 355
@@ -1802,38 +1808,32 @@ 

368 369 370 -371 -372 -373 -374 -375 -376 -377

@staticmethod
-def init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
-    r"""
-    Constructor of component.
-
-    Args:
-        cls: The component to construct.
-        *args: The arguments to pass to the component.
-        **kwargs: The keyword arguments to pass to the component.
-
-    Returns:
-        (Any):
-
-    Examples:
-        >>> class Module:
-        ...     def __init__(self, config, a=None, b=None):
-        ...         self.config = config
-        ...         self.a = config.a if a is None else a
-        ...         self.b = config.b if b is None else b
-        >>> config = NestedDict({"a": 0, "b": 1})
-        >>> module = ConfigRegistry.init(Module, config, b=2)
-        >>> module.a, module.b
-        (0, 2)
-    """
-
-    return cls(config, *args, **kwargs)
+371
@staticmethod
+def init(cls: Callable, config, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
+    r"""
+    Constructor of component.
+
+    Args:
+        cls: The component to construct.
+        *args: The arguments to pass to the component.
+        **kwargs: The keyword arguments to pass to the component.
+
+    Returns:
+        (Any):
+
+    Examples:
+        >>> class Module:
+        ...     def __init__(self, config, a=None, b=None):
+        ...         self.config = config
+        ...         self.a = config.a if a is None else a
+        ...         self.b = config.b if b is None else b
+        >>> config = NestedDict({"a": 0, "b": 1})
+        >>> module = ConfigRegistry.init(Module, config, b=2)
+        >>> module.a, module.b
+        (0, 2)
+    """
+
+    return cls(config, *args, **kwargs)
 
@@ -2180,13 +2180,7 @@

274 275 276 -277 -278 -279 -280 -281 -282 -283

class Registry(NestedDict):
+277
class Registry(NestedDict):
     """
     `Registry` for components.
 
@@ -2264,183 +2258,177 @@ 

super().__init__(fallback=fallback) if override is not None: self.setattr("override", override) - else: - self.setattr("override", self.override) - if key is not None: - self.setattr("key", key) - else: - self.setattr("key", self.key) - if default is not None: - self.setattr("default", default) - else: - self.setattr("default", self.default) + if key is not None: + self.setattr("key", key) + if default is not None: + self.setattr("default", default) + + def register( + self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False + ) -> Callable: + r""" + Register a new component. - def register( - self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False - ) -> Callable: - r""" - Register a new component. - - Args: - component: The component to register. - name: The name of the component. - - Returns: - component: The registered component. - Registered component are expected to be `Callable`. - - Raises: - ValueError: If the component with the same name already registered and `Registry.override=False`. - - Examples: - >>> registry = Registry() - >>> @registry.register - ... @registry.register("Module1") - ... class Module: - ... def __init__(self, a, b): - ... self.a = a - ... self.b = b - >>> module = registry.register(Module, "Module2") - >>> registry - Registry( - ('Module1'): <class 'chanfig.registry.Module'> - ('Module'): <class 'chanfig.registry.Module'> - ('Module2'): <class 'chanfig.registry.Module'> - ) - """ - - if name in self and not (override or self.override): - raise ValueError(f"Component with name {name} already registered.") - - # Registry.register() - if name is not Null: - self.set(name, component) + Args: + component: The component to register. + name: The name of the component. + + Returns: + component: The registered component. + Registered component are expected to be `Callable`. + + Raises: + ValueError: If the component with the same name already registered and `Registry.override=False`. + + Examples: + >>> registry = Registry() + >>> @registry.register + ... @registry.register("Module1") + ... class Module: + ... def __init__(self, a, b): + ... self.a = a + ... self.b = b + >>> module = registry.register(Module, "Module2") + >>> registry + Registry( + ('Module1'): <class 'chanfig.registry.Module'> + ('Module'): <class 'chanfig.registry.Module'> + ('Module2'): <class 'chanfig.registry.Module'> + ) + """ + + if name in self and not (override or self.override): + raise ValueError(f"Component with name {name} already registered.") + + # Registry.register() + if name is not Null: + self.set(name, component) + if default: + self.setattr("default", component) + return component + # @Registry.register + if component is not Null and callable(component) and name is Null: + self.set(component.__name__, component) if default: self.setattr("default", component) return component - # @Registry.register - if component is not Null and callable(component) and name is Null: - self.set(component.__name__, component) - if default: - self.setattr("default", component) - return component - - # @Registry.register() - def decorator(name: Any = Null): - @wraps(self.register) - def wrapper(component): - if name is Null: - self.set(component.__name__, component) - else: - self.set(name, component) - if default: - self.setattr("default", component) - return component - - return wrapper + + # @Registry.register() + def decorator(name: Any = Null): + @wraps(self.register) + def wrapper(component): + if name is Null: + self.set(component.__name__, component) + else: + self.set(name, component) + if default: + self.setattr("default", component) + return component + + return wrapper + + return decorator(component) + + def lookup(self, name: str, default: Any = Null) -> Any: + r""" + Lookup for a component. - return decorator(component) - - def lookup(self, name: str, default: Any = Null) -> Any: - r""" - Lookup for a component. + Args: + name: + + Returns: + (Any): The component. - Args: - name: + Raises: + KeyError: If the component is not registered. - Returns: - (Any): The component. - - Raises: - KeyError: If the component is not registered. - - Examples: - >>> registry = Registry() - >>> @registry.register - ... class Module: - ... def __init__(self, a, b): - ... self.a = a - ... self.b = b - >>> registry.lookup("Module") - <class 'chanfig.registry.Module'> - """ - - if default is Null: - default = self.getattr("default", Null) - return self.get(name, default) - - @staticmethod - def init(cls: Callable, *args: Any, **kwargs: Any) -> Any: # pylint: disable=W0211 - r""" - Constructor of component. - - Args: - cls: The component to construct. - *args: The arguments to pass to the component. - **kwargs: The keyword arguments to pass to the component. - - Returns: - (Any): - - Examples: - >>> class Module: - ... def __init__(self, a, b): - ... self.a = a - ... self.b = b - >>> kwargs = {"a": 0, "b": 1} - >>> module = Registry.init(Module, **kwargs) - >>> type(module) - <class 'chanfig.registry.Module'> - >>> module.a, module.b - (0, 1) - """ + Examples: + >>> registry = Registry() + >>> @registry.register + ... class Module: + ... def __init__(self, a, b): + ... self.a = a + ... self.b = b + >>> registry.lookup("Module") + <class 'chanfig.registry.Module'> + """ + + if default is Null: + default = self.getattr("default", Null) + return self.get(name, default) + + @staticmethod + def init(cls: Callable, *args: Any, **kwargs: Any) -> Any: # pylint: disable=W0211 + r""" + Constructor of component. + + Args: + cls: The component to construct. + *args: The arguments to pass to the component. + **kwargs: The keyword arguments to pass to the component. + + Returns: + (Any): + + Examples: + >>> class Module: + ... def __init__(self, a, b): + ... self.a = a + ... self.b = b + >>> kwargs = {"a": 0, "b": 1} + >>> module = Registry.init(Module, **kwargs) + >>> type(module) + <class 'chanfig.registry.Module'> + >>> module.a, module.b + (0, 1) + """ + + return cls(*args, **kwargs) + + def build(self, name: str | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any: + r""" + Build a component. - return cls(*args, **kwargs) - - def build(self, name: str | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any: - r""" - Build a component. - - Args: - name (str | MutableMapping): - If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`. - Note that values in `kwargs` will override values in `name` if its a `MutableMapping`. - *args: The arguments to pass to the component. - **kwargs: The keyword arguments to pass to the component. + Args: + name (str | MutableMapping): + If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`. + Note that values in `kwargs` will override values in `name` if its a `MutableMapping`. + *args: The arguments to pass to the component. + **kwargs: The keyword arguments to pass to the component. + + Returns: + (Any): + + Raises: + KeyError: If the component is not registered. - Returns: - (Any): - - Raises: - KeyError: If the component is not registered. - - Examples: - >>> registry = Registry(key="model") - >>> @registry.register - ... class Module: - ... def __init__(self, a, b): - ... self.a = a - ... self.b = b - >>> config = {"module": {"model": "Module", "a": 1, "b": 2}} - >>> # registry.register(Module) - >>> module = registry.build(**config["module"]) - >>> type(module) - <class 'chanfig.registry.Module'> - >>> module.a - 1 - >>> module.b - 2 - >>> module = registry.build(config["module"], a=2) - >>> module.a - 2 - """ - - if isinstance(name, MutableMapping): - name = deepcopy(name) - name, kwargs = name.pop(self.getattr("key", "name")), dict(name, **kwargs) # type: ignore[arg-type] - if name is Null: - name, kwargs = kwargs.pop(self.getattr("key"), None), dict(**kwargs) - return self.init(self.lookup(name), *args, **kwargs) # type: ignore[arg-type] + Examples: + >>> registry = Registry(key="model") + >>> @registry.register + ... class Module: + ... def __init__(self, a, b): + ... self.a = a + ... self.b = b + >>> config = {"module": {"model": "Module", "a": 1, "b": 2}} + >>> # registry.register(Module) + >>> module = registry.build(**config["module"]) + >>> type(module) + <class 'chanfig.registry.Module'> + >>> module.a + 1 + >>> module.b + 2 + >>> module = registry.build(config["module"], a=2) + >>> module.a + 2 + """ + + if isinstance(name, MutableMapping): + name = deepcopy(name) + name, kwargs = name.pop(self.getattr("key", "name")), dict(name, **kwargs) # type: ignore[arg-type] + if name is Null: + name, kwargs = kwargs.pop(self.getattr("key"), None), dict(**kwargs) + return self.init(self.lookup(name), *args, **kwargs) # type: ignore[arg-type]

@@ -2597,7 +2585,13 @@

Source code in chanfig/registry.py -
Python
240
+              
Python
234
+235
+236
+237
+238
+239
+240
 241
 242
 243
@@ -2634,56 +2628,50 @@ 

274 275 276 -277 -278 -279 -280 -281 -282 -283

def build(self, name: str | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any:
-    r"""
-    Build a component.
-
-    Args:
-        name (str | MutableMapping):
-            If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`.
-            Note that values in `kwargs` will override values in `name` if its a `MutableMapping`.
-        *args: The arguments to pass to the component.
-        **kwargs: The keyword arguments to pass to the component.
+277
def build(self, name: str | MutableMapping | NULL = Null, *args: Any, **kwargs: Any) -> Any:
+    r"""
+    Build a component.
+
+    Args:
+        name (str | MutableMapping):
+            If its a `MutableMapping`, it must contain `key` as a member, the rest will be treated as `**kwargs`.
+            Note that values in `kwargs` will override values in `name` if its a `MutableMapping`.
+        *args: The arguments to pass to the component.
+        **kwargs: The keyword arguments to pass to the component.
+
+    Returns:
+        (Any):
+
+    Raises:
+        KeyError: If the component is not registered.
 
-    Returns:
-        (Any):
-
-    Raises:
-        KeyError: If the component is not registered.
-
-    Examples:
-        >>> registry = Registry(key="model")
-        >>> @registry.register
-        ... class Module:
-        ...     def __init__(self, a, b):
-        ...         self.a = a
-        ...         self.b = b
-        >>> config = {"module": {"model": "Module", "a": 1, "b": 2}}
-        >>> # registry.register(Module)
-        >>> module = registry.build(**config["module"])
-        >>> type(module)
-        <class 'chanfig.registry.Module'>
-        >>> module.a
-        1
-        >>> module.b
-        2
-        >>> module = registry.build(config["module"], a=2)
-        >>> module.a
-        2
-    """
-
-    if isinstance(name, MutableMapping):
-        name = deepcopy(name)
-        name, kwargs = name.pop(self.getattr("key", "name")), dict(name, **kwargs)  # type: ignore[arg-type]
-    if name is Null:
-        name, kwargs = kwargs.pop(self.getattr("key"), None), dict(**kwargs)
-    return self.init(self.lookup(name), *args, **kwargs)  # type: ignore[arg-type]
+    Examples:
+        >>> registry = Registry(key="model")
+        >>> @registry.register
+        ... class Module:
+        ...     def __init__(self, a, b):
+        ...         self.a = a
+        ...         self.b = b
+        >>> config = {"module": {"model": "Module", "a": 1, "b": 2}}
+        >>> # registry.register(Module)
+        >>> module = registry.build(**config["module"])
+        >>> type(module)
+        <class 'chanfig.registry.Module'>
+        >>> module.a
+        1
+        >>> module.b
+        2
+        >>> module = registry.build(config["module"], a=2)
+        >>> module.a
+        2
+    """
+
+    if isinstance(name, MutableMapping):
+        name = deepcopy(name)
+        name, kwargs = name.pop(self.getattr("key", "name")), dict(name, **kwargs)  # type: ignore[arg-type]
+    if name is Null:
+        name, kwargs = kwargs.pop(self.getattr("key"), None), dict(**kwargs)
+    return self.init(self.lookup(name), *args, **kwargs)  # type: ignore[arg-type]
 
@@ -2803,7 +2791,13 @@

Source code in chanfig/registry.py -
Python
212
+              
Python
206
+207
+208
+209
+210
+211
+212
 213
 214
 215
@@ -2823,39 +2817,33 @@ 

229 230 231 -232 -233 -234 -235 -236 -237 -238

@staticmethod
-def init(cls: Callable, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
-    r"""
-    Constructor of component.
-
-    Args:
-        cls: The component to construct.
-        *args: The arguments to pass to the component.
-        **kwargs: The keyword arguments to pass to the component.
-
-    Returns:
-        (Any):
-
-    Examples:
-        >>> class Module:
-        ...     def __init__(self, a, b):
-        ...         self.a = a
-        ...         self.b = b
-        >>> kwargs = {"a": 0, "b": 1}
-        >>> module = Registry.init(Module, **kwargs)
-        >>> type(module)
-        <class 'chanfig.registry.Module'>
-        >>> module.a, module.b
-        (0, 1)
-    """
-
-    return cls(*args, **kwargs)
+232
@staticmethod
+def init(cls: Callable, *args: Any, **kwargs: Any) -> Any:  # pylint: disable=W0211
+    r"""
+    Constructor of component.
+
+    Args:
+        cls: The component to construct.
+        *args: The arguments to pass to the component.
+        **kwargs: The keyword arguments to pass to the component.
+
+    Returns:
+        (Any):
+
+    Examples:
+        >>> class Module:
+        ...     def __init__(self, a, b):
+        ...         self.a = a
+        ...         self.b = b
+        >>> kwargs = {"a": 0, "b": 1}
+        >>> module = Registry.init(Module, **kwargs)
+        >>> type(module)
+        <class 'chanfig.registry.Module'>
+        >>> module.a, module.b
+        (0, 1)
+    """
+
+    return cls(*args, **kwargs)
 
@@ -2964,7 +2952,13 @@

Source code in chanfig/registry.py -
Python
184
+              
Python
178
+179
+180
+181
+182
+183
+184
 185
 186
 187
@@ -2984,39 +2978,33 @@ 

201 202 203 -204 -205 -206 -207 -208 -209 -210

def lookup(self, name: str, default: Any = Null) -> Any:
-    r"""
-    Lookup for a component.
+204
def lookup(self, name: str, default: Any = Null) -> Any:
+    r"""
+    Lookup for a component.
+
+    Args:
+        name:
+
+    Returns:
+        (Any): The component.
 
-    Args:
-        name:
+    Raises:
+        KeyError: If the component is not registered.
 
-    Returns:
-        (Any): The component.
-
-    Raises:
-        KeyError: If the component is not registered.
-
-    Examples:
-        >>> registry = Registry()
-        >>> @registry.register
-        ... class Module:
-        ...     def __init__(self, a, b):
-        ...         self.a = a
-        ...         self.b = b
-        >>> registry.lookup("Module")
-        <class 'chanfig.registry.Module'>
-    """
-
-    if default is Null:
-        default = self.getattr("default", Null)
-    return self.get(name, default)
+    Examples:
+        >>> registry = Registry()
+        >>> @registry.register
+        ... class Module:
+        ...     def __init__(self, a, b):
+        ...         self.a = a
+        ...         self.b = b
+        >>> registry.lookup("Module")
+        <class 'chanfig.registry.Module'>
+    """
+
+    if default is Null:
+        default = self.getattr("default", Null)
+    return self.get(name, default)
 
@@ -3146,7 +3134,13 @@

Source code in chanfig/registry.py -
Python
118
+              
Python
112
+113
+114
+115
+116
+117
+118
 119
 120
 121
@@ -3204,77 +3198,71 @@ 

173 174 175 -176 -177 -178 -179 -180 -181 -182

def register(
-    self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False
-) -> Callable:
-    r"""
-    Register a new component.
-
-    Args:
-        component: The component to register.
-        name: The name of the component.
-
-    Returns:
-        component: The registered component.
-            Registered component are expected to be `Callable`.
-
-    Raises:
-        ValueError: If the component with the same name already registered and `Registry.override=False`.
-
-    Examples:
-        >>> registry = Registry()
-        >>> @registry.register
-        ... @registry.register("Module1")
-        ... class Module:
-        ...     def __init__(self, a, b):
-        ...         self.a = a
-        ...         self.b = b
-        >>> module = registry.register(Module, "Module2")
-        >>> registry
-        Registry(
-          ('Module1'): <class 'chanfig.registry.Module'>
-          ('Module'): <class 'chanfig.registry.Module'>
-          ('Module2'): <class 'chanfig.registry.Module'>
-        )
-    """
-
-    if name in self and not (override or self.override):
-        raise ValueError(f"Component with name {name} already registered.")
-
-    # Registry.register()
-    if name is not Null:
-        self.set(name, component)
+176
def register(
+    self, component: Any = Null, name: Any = Null, override: bool = False, default: bool = False
+) -> Callable:
+    r"""
+    Register a new component.
+
+    Args:
+        component: The component to register.
+        name: The name of the component.
+
+    Returns:
+        component: The registered component.
+            Registered component are expected to be `Callable`.
+
+    Raises:
+        ValueError: If the component with the same name already registered and `Registry.override=False`.
+
+    Examples:
+        >>> registry = Registry()
+        >>> @registry.register
+        ... @registry.register("Module1")
+        ... class Module:
+        ...     def __init__(self, a, b):
+        ...         self.a = a
+        ...         self.b = b
+        >>> module = registry.register(Module, "Module2")
+        >>> registry
+        Registry(
+          ('Module1'): <class 'chanfig.registry.Module'>
+          ('Module'): <class 'chanfig.registry.Module'>
+          ('Module2'): <class 'chanfig.registry.Module'>
+        )
+    """
+
+    if name in self and not (override or self.override):
+        raise ValueError(f"Component with name {name} already registered.")
+
+    # Registry.register()
+    if name is not Null:
+        self.set(name, component)
+        if default:
+            self.setattr("default", component)
+        return component
+    # @Registry.register
+    if component is not Null and callable(component) and name is Null:
+        self.set(component.__name__, component)
         if default:
             self.setattr("default", component)
         return component
-    # @Registry.register
-    if component is not Null and callable(component) and name is Null:
-        self.set(component.__name__, component)
-        if default:
-            self.setattr("default", component)
-        return component
-
-    # @Registry.register()
-    def decorator(name: Any = Null):
-        @wraps(self.register)
-        def wrapper(component):
-            if name is Null:
-                self.set(component.__name__, component)
-            else:
-                self.set(name, component)
-            if default:
-                self.setattr("default", component)
-            return component
-
-        return wrapper
-
-    return decorator(component)
+
+    # @Registry.register()
+    def decorator(name: Any = Null):
+        @wraps(self.register)
+        def wrapper(component):
+            if name is Null:
+                self.set(component.__name__, component)
+            else:
+                self.set(name, component)
+            if default:
+                self.setattr("default", component)
+            return component
+
+        return wrapper
+
+    return decorator(component)