qmk_firmware/coding_conventions_python.html

88 lines
119 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="en-US" dir="ltr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Coding Conventions (Python) | QMK Firmware</title>
<meta name="description" content="Documentation for QMK Firmware">
<meta name="generator" content="VitePress v1.1.3">
<link rel="preload stylesheet" href="/assets/style.No8zk9aC.css" as="style">
<script type="module" src="/assets/app.D828SKzM.js"></script>
<link rel="preload" href="/assets/inter-roman-latin.Di8DUHzh.woff2" as="font" type="font/woff2" crossorigin="">
<link rel="modulepreload" href="/assets/chunks/framework.DyMmIvSC.js">
<link rel="modulepreload" href="/assets/chunks/theme.D-bb5p4x.js">
<link rel="modulepreload" href="/assets/coding_conventions_python.md.BV-OK1s5.lean.js">
<script id="check-dark-mode">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"auto",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
<script id="check-mac-os">document.documentElement.classList.toggle("mac",/Mac|iPhone|iPod|iPad/i.test(navigator.platform));</script>
</head>
<body>
<div id="app"><div class="Layout" data-v-5d98c3a5><!--[--><!--]--><!--[--><span tabindex="-1" data-v-0f60ec36></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-0f60ec36> Skip to content </a><!--]--><!----><header class="VPNav" data-v-5d98c3a5 data-v-ae24b3ad><div class="VPNavBar has-sidebar top" data-v-ae24b3ad data-v-ccf7ddec><div class="wrapper" data-v-ccf7ddec><div class="container" data-v-ccf7ddec><div class="title" data-v-ccf7ddec><div class="VPNavBarTitle has-sidebar" data-v-ccf7ddec data-v-ab179fa1><a class="title" href="/" data-v-ab179fa1><!--[--><!--]--><!--[--><!--[--><!--[--><img class="VPImage dark logo" src="/qmk-logo-dark.svg" alt data-v-8426fc1a><!--]--><!--[--><img class="VPImage light logo" src="/qmk-logo-light.svg" alt data-v-8426fc1a><!--]--><!--]--><!--]--><span data-v-ab179fa1>QMK Firmware</span><!--[--><!--]--></a></div></div><div class="content" data-v-ccf7ddec><div class="content-body" data-v-ccf7ddec><!--[--><!--]--><div class="VPNavBarSearch search" data-v-ccf7ddec><!--[--><!----><div id="local-search"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><span class="vp-icon DocSearch-Search-Icon"></span><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-ccf7ddec data-v-7f418b0f><span id="main-nav-aria-label" class="visually-hidden" data-v-7f418b0f>Main Navigation</span><!--[--><!--[--><a class="VPLink link VPNavBarMenuLink" href="./" tabindex="0" data-v-7f418b0f data-v-9c663999><!--[--><span data-v-9c663999>Home</span><!--]--></a><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-ccf7ddec data-v-e6aabb21><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="Switch to dark theme" aria-checked="false" data-v-e6aabb21 data-v-d1f28634 data-v-1d5665e3><span class="check" data-v-1d5665e3><span class="icon" data-v-1d5665e3><!--[--><span class="vpi-sun sun" data-v-d1f28634></span><span class="vpi-moon moon" data-v-d1f28634></span><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-ccf7ddec data-v-0394ad82 data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://reddit.com/r/olkb" aria-label target="_blank" rel="noopener" data-v-7bc22406 data-v-eee4e7cb><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 29 3 C 28.0625 3 27.164063 3.382813 26.5 4 C 25.835938 4.617188 25.363281 5.433594 25 6.40625 C 24.355469 8.140625 24.085938 10.394531 24.03125 13.03125 C 19.234375 13.179688 14.820313 14.421875 11.28125 16.46875 C 10.214844 15.46875 8.855469 14.96875 7.5 14.96875 C 6.089844 14.96875 4.675781 15.511719 3.59375 16.59375 C 1.425781 18.761719 1.425781 22.238281 3.59375 24.40625 L 3.84375 24.65625 C 3.3125 26.035156 3 27.488281 3 29 C 3 33.527344 5.566406 37.585938 9.5625 40.4375 C 13.558594 43.289063 19.007813 45 25 45 C 30.992188 45 36.441406 43.289063 40.4375 40.4375 C 44.433594 37.585938 47 33.527344 47 29 C 47 27.488281 46.6875 26.035156 46.15625 24.65625 L 46.40625 24.40625 C 48.574219 22.238281 48.574219 18.761719 46.40625 16.59375 C 45.324219 15.511719 43.910156 14.96875 42.5 14.96875 C 41.144531 14.96875 39.785156 15.46875 38.71875 16.46875 C 35.195313 14.433594 30.800781 13.191406 26.03125 13.03125 C 26.09375 10.546875 26.363281 8.46875 26.875 7.09375 C 27.164063 6.316406 27.527344 5.757813 27.875 5.4375 C 28.222656 5.117188 28.539063 5 29 5 C 29.460938 5 29.683594 5.125 30.03125 5.40625 C 30.378906 5.6875 30.785156 6.148438 31.3125 6.6875 C 32.253906 7.652344 33.695313 8.714844 36.09375 8.9375 C 36.539063 11.238281 38.574219 13 41 13 C 43.75 13 46 10.75 46 8 C 46 5.25 43.75 3 41 3 C 38.605469 3 36.574219 4.710938 36.09375 6.96875 C 34.3125 6.796875 33.527344 6.109375 32.75 5.3125 C 32.300781 4.851563 31.886719
<span class="line"><span></span></span>
<span class="line"><span>effects.echo()</span></span></code></pre></div><p>Bad:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>from qmk.effects import echo</span></span>
<span class="line"><span></span></span>
<span class="line"><span>echo() # It&#39;s unclear where echo comes from</span></span></code></pre></div><p>Good:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>from qmk.keymap import compile_firmware</span></span>
<span class="line"><span></span></span>
<span class="line"><span>compile_firmware()</span></span></code></pre></div><p>OK, but the above is better:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>import qmk.keymap</span></span>
<span class="line"><span></span></span>
<span class="line"><span>qmk.keymap.compile_firmware()</span></span></code></pre></div><h1 id="statements" tabindex="-1">Statements <a class="header-anchor" href="#statements" aria-label="Permalink to &quot;Statements&quot;"></a></h1><p>One statement per line.</p><p>Even when allowed (EG <code>if foo: bar</code>) we do not combine 2 statements onto a single line.</p><h1 id="naming" tabindex="-1">Naming <a class="header-anchor" href="#naming" aria-label="Permalink to &quot;Naming&quot;"></a></h1><p><code>module_name</code>, <code>package_name</code>, <code>ClassName</code>, <code>method_name</code>, <code>ExceptionName</code>, <code>function_name</code>, <code>GLOBAL_CONSTANT_NAME</code>, <code>global_var_name</code>, <code>instance_var_name</code>, <code>function_parameter_name</code>, <code>local_var_name</code>.</p><p>Function names, variable names, and filenames should be descriptive; eschew abbreviation. In particular, do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word.</p><p>Always use a .py filename extension. Never use dashes.</p><h2 id="names-to-avoid" tabindex="-1">Names to Avoid <a class="header-anchor" href="#names-to-avoid" aria-label="Permalink to &quot;Names to Avoid&quot;"></a></h2><ul><li>single character names except for counters or iterators. You may use <code>e</code> as an exception identifier in try/except statements.</li><li>dashes (<code>-</code>) in any package/module name</li><li><code>__double_leading_and_trailing_underscore__</code> names (reserved by Python)</li></ul><h1 id="docstrings" tabindex="-1">Docstrings <a class="header-anchor" href="#docstrings" aria-label="Permalink to &quot;Docstrings&quot;"></a></h1><p>To maintain consistency with our docstrings we&#39;ve set out the following guidelines.</p><ul><li>Use markdown formatting</li><li>Always use triple-dquote docstrings with at least one linebreak: <code>&quot;&quot;&quot;\n&quot;&quot;&quot;</code></li><li>First line is a short (&lt; 70 char) description of what the function does</li><li>If you need more in your docstring leave a blank line between the description and the rest.</li><li>Start indented lines at the same indent level as the opening triple-dquote</li><li>Document all function arguments using the format described below</li><li>If present, Args:, Returns:, and Raises: should be the last three things in the docstring, separated by a blank line each.</li></ul><h2 id="simple-docstring-example" tabindex="-1">Simple docstring example <a class="header-anchor" href="#simple-docstring-example" aria-label="Permalink to &quot;Simple docstring example&quot;"></a></h2><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>def my_awesome_function():</span></span>
<span class="line"><span> &quot;&quot;&quot;Return the number of seconds since 1970 Jan 1 00:00 UTC.</span></span>
<span class="line"><span> &quot;&quot;&quot;</span></span>
<span class="line"><span> return int(time.time())</span></span></code></pre></div><h2 id="complex-docstring-example" tabindex="-1">Complex docstring example <a class="header-anchor" href="#complex-docstring-example" aria-label="Permalink to &quot;Complex docstring example&quot;"></a></h2><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>def my_awesome_function():</span></span>
<span class="line"><span> &quot;&quot;&quot;Return the number of seconds since 1970 Jan 1 00:00 UTC.</span></span>
<span class="line"><span></span></span>
<span class="line"><span> This function always returns an integer number of seconds.</span></span>
<span class="line"><span> &quot;&quot;&quot;</span></span>
<span class="line"><span> return int(time.time())</span></span></code></pre></div><h2 id="function-arguments-docstring-example" tabindex="-1">Function arguments docstring example <a class="header-anchor" href="#function-arguments-docstring-example" aria-label="Permalink to &quot;Function arguments docstring example&quot;"></a></h2><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>def my_awesome_function(start=None, offset=0):</span></span>
<span class="line"><span> &quot;&quot;&quot;Return the number of seconds since 1970 Jan 1 00:00 UTC.</span></span>
<span class="line"><span></span></span>
<span class="line"><span> This function always returns an integer number of seconds.</span></span>
<span class="line"><span></span></span>
<span class="line"><span></span></span>
<span class="line"><span> Args:</span></span>
<span class="line"><span> start</span></span>
<span class="line"><span> The time to start at instead of 1970 Jan 1 00:00 UTC</span></span>
<span class="line"><span></span></span>
<span class="line"><span> offset</span></span>
<span class="line"><span> Return an answer that has this number of seconds subtracted first</span></span>
<span class="line"><span></span></span>
<span class="line"><span> Returns:</span></span>
<span class="line"><span> An integer describing a number of seconds.</span></span>
<span class="line"><span></span></span>
<span class="line"><span> Raises:</span></span>
<span class="line"><span> ValueError</span></span>
<span class="line"><span> When `start` or `offset` are not positive numbers</span></span>
<span class="line"><span> &quot;&quot;&quot;</span></span>
<span class="line"><span> if start &lt; 0 or offset &lt; 0:</span></span>
<span class="line"><span> raise ValueError(&#39;start and offset must be positive numbers.&#39;)</span></span>
<span class="line"><span></span></span>
<span class="line"><span> if not start:</span></span>
<span class="line"><span> start = time.time()</span></span>
<span class="line"><span></span></span>
<span class="line"><span> return int(start - offset)</span></span></code></pre></div><h1 id="exceptions" tabindex="-1">Exceptions <a class="header-anchor" href="#exceptions" aria-label="Permalink to &quot;Exceptions&quot;"></a></h1><p>Exceptions are used to handle exceptional situations. They should not be used for flow control. This is a break from the python norm of &quot;ask for forgiveness.&quot; If you are catching an exception it should be to handle a situation that is unusual.</p><p>If you use a catch-all exception for any reason you must log the exception and stacktrace using cli.log.</p><p>Make your try/except blocks as short as possible. If you need a lot of try statements you may need to restructure your code.</p><h1 id="tuples" tabindex="-1">Tuples <a class="header-anchor" href="#tuples" aria-label="Permalink to &quot;Tuples&quot;"></a></h1><p>When defining one-item tuples always include a trailing comma so that it is obvious you are using a tuple. Do not rely on implicit one-item tuple unpacking. Better still use a list which is unambiguous.</p><p>This is particularly important when using the printf-style format strings that are commonly used.</p><h1 id="lists-and-dictionaries" tabindex="-1">Lists and Dictionaries <a class="header-anchor" href="#lists-and-dictionaries" aria-label="Permalink to &quot;Lists and Dictionaries&quot;"></a></h1><p>We have configured YAPF to differentiate between sequence styles with a trailing comma. When a trailing comma is omitted YAPF will format the sequence as a single line. When a trailing comma is included YAPF will format the sequence with one item per line.</p><p>You should generally prefer to keep short definition on a single line. Break out to multiple lines sooner rather than later to aid readability and maintainability.</p><h1 id="parentheses" tabindex="-1">Parentheses <a class="header-anchor" href="#parentheses" aria-label="Permalink to &quot;Parentheses&quot;"></a></h1><p>Avoid excessive parentheses, but do use parentheses to make code easier to understand. Do not use them in return statements unless you are explicitly returning a tuple, or it is part of a math expression.</p><h1 id="format-strings" tabindex="-1">Format Strings <a class="header-anchor" href="#format-strings" aria-label="Permalink to &quot;Format Strings&quot;"></a></h1><p>We generally prefer printf-style format strings. Example:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>name = &#39;World&#39;</span></span>
<span class="line"><span>print(&#39;Hello, %s!&#39; % (name,))</span></span></code></pre></div><p>This style is used by the logging module, which we make use of extensively, and we have adopted it in other places for consistency. It is also more familiar to C programmers, who are a big part of our casual audience.</p><p>Our included CLI module has support for using these without using the percent (%) operator. Look at <code>cli.echo()</code> and the various <code>cli.log</code> functions (EG, <code>cli.log.info()</code>) for more details.</p><h1 id="comprehensions-generator-expressions" tabindex="-1">Comprehensions &amp; Generator Expressions <a class="header-anchor" href="#comprehensions-generator-expressions" aria-label="Permalink to &quot;Comprehensions &amp; Generator Expressions&quot;"></a></h1><p>We encourage the liberal use of comprehensions and generators, but do not let them get too complex. If you need complexity fall back to a for loop that is easier to understand.</p><h1 id="lambdas" tabindex="-1">Lambdas <a class="header-anchor" href="#lambdas" aria-label="Permalink to &quot;Lambdas&quot;"></a></h1><p>OK to use but probably should be avoided. With comprehensions and generators the need for lambdas is not as strong as it once was.</p><h1 id="conditional-expressions" tabindex="-1">Conditional Expressions <a class="header-anchor" href="#conditional-expressions" aria-label="Permalink to &quot;Conditional Expressions&quot;"></a></h1><p>OK in variable assignment, but otherwise should be avoided.</p><p>Conditional expressions are if statements that are in line with code. For example:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>x = 1 if cond else 2</span></span></code></pre></div><p>It&#39;s generally not a good idea to use these as function arguments, sequence items, etc. It&#39;s too easy to overlook.</p><h1 id="default-argument-values" tabindex="-1">Default Argument Values <a class="header-anchor" href="#default-argument-values" aria-label="Permalink to &quot;Default Argument Values&quot;"></a></h1><p>Encouraged, but values must be immutable objects.</p><p>When specifying default values in argument lists always be careful to specify objects that can&#39;t be modified in place. If you use a mutable object the changes you make will persist between calls, which is usually not what you want. Even if that is what you intend to do it is confusing for others and will hinder understanding.</p><p>Bad:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>def my_func(foo={}):</span></span>
<span class="line"><span> pass</span></span></code></pre></div><p>Good:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>def my_func(foo=None):</span></span>
<span class="line"><span> if not foo:</span></span>
<span class="line"><span> foo = {}</span></span></code></pre></div><h1 id="properties" tabindex="-1">Properties <a class="header-anchor" href="#properties" aria-label="Permalink to &quot;Properties&quot;"></a></h1><p>Always use properties instead of getter and setter functions.</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>class Foo(object):</span></span>
<span class="line"><span> def __init__(self):</span></span>
<span class="line"><span> self._bar = None</span></span>
<span class="line"><span></span></span>
<span class="line"><span> @property</span></span>
<span class="line"><span> def bar(self):</span></span>
<span class="line"><span> return self._bar</span></span>
<span class="line"><span></span></span>
<span class="line"><span> @bar.setter</span></span>
<span class="line"><span> def bar(self, bar):</span></span>
<span class="line"><span> self._bar = bar</span></span></code></pre></div><h1 id="true-false-evaluations" tabindex="-1">True/False Evaluations <a class="header-anchor" href="#true-false-evaluations" aria-label="Permalink to &quot;True/False Evaluations&quot;"></a></h1><p>You should generally prefer the implicit True/False evaluation in if statements, rather than checking equivalency.</p><p>Bad:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>if foo == True:</span></span>
<span class="line"><span> pass</span></span>
<span class="line"><span></span></span>
<span class="line"><span>if bar == False:</span></span>
<span class="line"><span> pass</span></span></code></pre></div><p>Good:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>if foo:</span></span>
<span class="line"><span> pass</span></span>
<span class="line"><span></span></span>
<span class="line"><span>if not bar:</span></span>
<span class="line"><span> pass</span></span></code></pre></div><h1 id="decorators" tabindex="-1">Decorators <a class="header-anchor" href="#decorators" aria-label="Permalink to &quot;Decorators&quot;"></a></h1><p>Use when appropriate. Try to avoid too much magic unless it helps with understanding.</p><h1 id="threading-and-multiprocessing" tabindex="-1">Threading and Multiprocessing <a class="header-anchor" href="#threading-and-multiprocessing" aria-label="Permalink to &quot;Threading and Multiprocessing&quot;"></a></h1><p>Should be avoided. If you need this you will have to make a strong case before we merge your code.</p><h1 id="power-features" tabindex="-1">Power Features <a class="header-anchor" href="#power-features" aria-label="Permalink to &quot;Power Features&quot;"></a></h1><p>Python is an extremely flexible language and gives you many fancy features such as custom metaclasses, access to bytecode, on-the-fly compilation, dynamic inheritance, object reparenting, import hacks, reflection, modification of system internals, etc.</p><p>Don&#39;t use these.</p><p>Performance is not a critical concern for us, and code understandability is. We want our codebase to be approachable by someone who only has a day or two to play with it. These features generally come with a cost to easy understanding, and we would prefer to have code that can be readily understood over faster or more compact code.</p><p>Note that some standard library modules use these techniques and it is ok to make use of those modules. But please keep readability and understandability in mind when using them.</p><h1 id="type-annotated-code" tabindex="-1">Type Annotated Code <a class="header-anchor" href="#type-annotated-code" aria-label="Permalink to &quot;Type Annotated Code&quot;"></a></h1><p>For now we are not using any type annotation system, and would prefer that code remain unannotated. We may revisit this in the future.</p><h1 id="function-length" tabindex="-1">Function length <a class="header-anchor" href="#function-length" aria-label="Permalink to &quot;Function length&quot;"></a></h1><p>Prefer small and focused functions.</p><p>We recognize that long functions are sometimes appropriate, so no hard limit is placed on function length. If a function exceeds about 40 lines, think about whether it can be broken up without harming the structure of the program.</p><p>Even if your long function works perfectly now, someone modifying it in a few months may add new behavior. This could result in bugs that are hard to find. Keeping your functions short and simple makes it easier for other people to read and modify your code.</p><p>You could find long and complicated functions when working with some code. Do not be intimidated by modifying existing code: if working with such a function proves to be difficult, you find that errors are hard to debug, or you want to use a piece of it in several different contexts, consider breaking up the function into smaller and more manageable pieces.</p><h1 id="fixmes" tabindex="-1">FIXMEs <a class="header-anchor" href="#fixmes" aria-label="Permalink to &quot;FIXMEs&quot;"></a></h1><p>It is OK to leave FIXMEs in code. Why? Encouraging people to at least document parts of code that need to be thought out more (or that are confusing) is better than leaving this code undocumented.</p><p>All FIXMEs should be formatted like:</p><div class="language- vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki shiki-themes github-light github-dark vp-code"><code><span class="line"><span>FIXME(username): Revisit this code when the frob feature is done.</span></span></code></pre></div><p>...where username is your GitHub username.</p><h1 id="testing" tabindex="-1">Testing <a class="header-anchor" href="#testing" aria-label="Permalink to &quot;Testing&quot;"></a></h1><p>We use a combination of Integration and Unit testing to ensure that the our code is as bug-free as possible. All the tests can be found in <code>lib/python/qmk/tests/</code>. You can run all th
<script>window.__VP_HASH_MAP__=JSON.parse("{\"changelog_20200530.md\":\"Dk-vRpTQ\",\"changelog_20200229.md\":\"DNsowwM1\",\"changelog_20200829.md\":\"CoqrOffn\",\"changelog_20210529.md\":\"CR1YNfZX\",\"cli_configuration.md\":\"CGSB128P\",\"config_options.md\":\"DvMeqlpB\",\"cli.md\":\"BLzAdA6l\",\"configurator_architecture.md\":\"DEFTvTlS\",\"cli_commands.md\":\"DGx-qNiE\",\"drivers_serial.md\":\"qxjytVdM\",\"drivers_spi.md\":\"tJVeIT7e\",\"drivers_uart.md\":\"BNBs-m3h\",\"drivers_ws2812.md\":\"D9E3Wb-Y\",\"changelog_20230827.md\":\"NSPhmB6l\",\"changelog_20231126.md\":\"D2Ok5QAf\",\"api_overview.md\":\"0FPaUsMb\",\"driver_installation_zadig.md\":\"CX9-BACt\",\"breaking_changes_history.md\":\"CkzdD6x8\",\"breaking_changes_instructions.md\":\"BAjIGeJb\",\"newbs_testing_debugging.md\":\"D-c7Gle_\",\"features_sequencer.md\":\"DkgqNCsQ\",\"drivers_apa102.md\":\"Qb-KXRpF\",\"one_shot_keys.md\":\"QR09rSb-\",\"arm_debugging.md\":\"BH-H2Ukz\",\"feature_userspace.md\":\"BsmY9yWw\",\"features_unicode.md\":\"CsON-fJH\",\"coding_conventions_c.md\":\"DdrpXBEh\",\"chibios_upgrade_instructions.md\":\"B5dMtL5R\",\"changelog_20201128.md\":\"7XXL02Bn\",\"changelog_20210828.md\":\"DNbHIuuM\",\"cli_development.md\":\"3xFA2OvS\",\"faq_keymap.md\":\"BsEmEZTo\",\"other_eclipse.md\":\"C-zqyJO9\",\"drivers_gpio.md\":\"C5PvyJVh\",\"other_vscode.md\":\"Di3vOySR\",\"changelog_20220226.md\":\"BWO5r_ec\",\"changelog_20240526.md\":\"iGLFD0SP\",\"features_space_cadet.md\":\"DmNfYRVJ\",\"faq_misc.md\":\"qGrhOTu4\",\"features_stenography.md\":\"Bb_IhUGl\",\"features_st7565.md\":\"MoajNurJ\",\"features_ps2_mouse.md\":\"CL_pA9sk\",\"features_split_keyboard.md\":\"mMTUzI5n\",\"feature_eeprom.md\":\"a0eoTZm7\",\"changelog_20220827.md\":\"DjPthqDk\",\"drivers_audio.md\":\"mqaz6p5N\",\"drivers_eeprom.md\":\"B_6soqar\",\"drivers_flash.md\":\"DhWcHp8S\",\"changelog_20221126.md\":\"DpbrnXeC\",\"drivers_i2c.md\":\"Cuj87reB\",\"api_development_overview.md\":\"Dcey4ntL\",\"features_wpm.md\":\"BYf-Xc1H\",\"flashing.md\":\"BSZF2RCy\",\"configurator_step_by_step.md\":\"GvmPtScT\",\"faq_build.md\":\"DL_WezA1\",\"api_docs.md\":\"CRoD6CbL\",\"platformdev_rp2040.md\":\"CYKYMDfc\",\"porting_your_keyboard_to_qmk.md\":\"CGlGdFj4\",\"faq_general.md\":\"BC3pFw4U\",\"data_driven_config.md\":\"BvLQ7P20\",\"custom_matrix.md\":\"DMnRw_5l\",\"faq_debug.md\":\"DuThValw\",\"pr_checklist.md\":\"BnrDXG2G\",\"documentation_templates.md\":\"Bhkum0wD\",\"drivers_adc.md\":\"DzU8txf8\",\"platformdev_selecting_arm_mcu.md\":\"aF4DUsvb\",\"getting_started_docker.md\":\"mz6HE4Bl\",\"features_caps_word.md\":\"BDQGM0-x\",\"configurator_troubleshooting.md\":\"BxGLPT8G\",\"features_swap_hands.md\":\"DOhgGQf9\",\"features_dip_switch.md\":\"DtBOdc98\",\"features_tri_layer.md\":\"DdnBVinw\",\"quantum_painter_lvgl.md\":\"DobNbSgA\",\"feature_debounce_type.md\":\"CUrSp0Iy\",\"features_dynamic_macros.md\":\"CrJ1vhBA\",\"quantum_painter_rle.md\":\"B07V3m-n\",\"features_encoders.md\":\"B2SX3hG2\",\"features_tap_dance.md\":\"CKLVpnIn\",\"getting_started_make_guide.md\":\"BjUuEnSh\",\"reference_glossary.md\":\"CXMuxToT\",\"features_grave_esc.md\":\"mUNEjUYS\",\"reference_keymap_extras.md\":\"-n_bLm78\",\"configurator_default_keymaps.md\":\"B3v8dlHd\",\"feature_layouts.md\":\"qcy8hNMO\",\"changelog_20230528.md\":\"nX9R48zP\",\"changelog_20240225.md\":\"B4-WrXj6\",\"features_audio.md\":\"DWNYte3W\",\"contributing.md\":\"DLsp4vSA\",\"features_hd44780.md\":\"15K40o_e\",\"features_key_lock.md\":\"IjFq9dYs\",\"features_key_overrides.md\":\"rVTP6kn9\",\"features_leader_key.md\":\"DBQTnBRh\",\"features_led_indicators.md\":\"CCZhjHe9\",\"features_mouse_keys.md\":\"C3P3pYuE\",\"features_midi.md\":\"u3tHPteB\",\"features_led_matrix.md\":\"DV31nHH-\",\"features_repeat_key.md\":\"C3eWDUWn\",\"features_rawhid.md\":\"bKKG3KCa\",\"features_oled_driver.md\":\"D9wJVdL1\",\"documentation_best_practices.md\":\"BtEjePaZ\",\"getting_started_github.md\":\"OYq7IT7B\",\"getting_started_introduction.md\":\"BOJreqSs\",\"features_secure.md\":\"BX7jFvW5\",\"features_rgblight.md\":\"DehQikiL\",\"hardware_drivers.md\":\"BUnx48D9\",\"h
</body>
</html>