<spanclass="line"><span>echo() # It's unclear where echo comes from</span></span></code></pre></div><p>Good:</p><divclass="language- vp-adaptive-theme"><buttontitle="Copy Code"class="copy"></button><spanclass="lang"></span><preclass="shiki shiki-themes github-light github-dark vp-code"><code><spanclass="line"><span>from qmk.keymap import compile_firmware</span></span>
<spanclass="line"><span></span></span>
<spanclass="line"><span>compile_firmware()</span></span></code></pre></div><p>OK, but the above is better:</p><divclass="language- vp-adaptive-theme"><buttontitle="Copy Code"class="copy"></button><spanclass="lang"></span><preclass="shiki shiki-themes github-light github-dark vp-code"><code><spanclass="line"><span>import qmk.keymap</span></span>
<spanclass="line"><span></span></span>
<spanclass="line"><span>qmk.keymap.compile_firmware()</span></span></code></pre></div><h1id="statements"tabindex="-1">Statements <aclass="header-anchor"href="#statements"aria-label="Permalink to "Statements""></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><h1id="naming"tabindex="-1">Naming <aclass="header-anchor"href="#naming"aria-label="Permalink to "Naming""></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><h2id="names-to-avoid"tabindex="-1">Names to Avoid <aclass="header-anchor"href="#names-to-avoid"aria-label="Permalink to "Names to Avoid""></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><h1id="docstrings"tabindex="-1">Docstrings <aclass="header-anchor"href="#docstrings"aria-label="Permalink to "Docstrings""></a></h1><p>To maintain consistency with our docstrings we'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>"""\n"""</code></li><li>First line is a short (< 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><h2id="simple-docstring-example"tabindex="-1">Simple docstring example <aclass="header-anchor"href="#simple-docstring-example"aria-label="Permalink to "Simple docstring example""></a></h2><divclass="language- vp-adaptive-theme"><buttontitle="Copy Code"class="copy"></button><spanclass="lang"></span><preclass="shiki shiki-themes github-light github-dark vp-code"><code><spanclass="line"><span>def my_awesome_function():</span></span>
<spanclass="line"><span>"""Return the number of seconds since 1970 Jan 1 00:00 UTC.</span></span>
<spanclass="line"><span> return int(start - offset)</span></span></code></pre></div><h1id="exceptions"tabindex="-1">Exceptions <aclass="header-anchor"href="#exceptions"aria-label="Permalink to "Exceptions""></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 "ask for forgiveness." 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><h1id="tuples"tabindex="-1">Tuples <aclass="header-anchor"href="#tuples"aria-label="Permalink to "Tuples""></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><h1id="lists-and-dictionaries"tabindex="-1">Lists and Dictionaries <aclass="header-anchor"href="#lists-and-dictionaries"aria-label="Permalink to "Lists and Dictionaries""></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><h1id="parentheses"tabindex="-1">Parentheses <aclass="header-anchor"href="#parentheses"aria-label="Permalink to "Parentheses""></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><h1id="format-strings"tabindex="-1">Format Strings <aclass="header-anchor"href="#format-strings"aria-label="Permalink to "Format Strings""></a></h1><p>We generally prefer printf-style format strings. Example:</p><divclass="language- vp-adaptive-theme"><buttontitle="Copy Code"class="copy"></button><spanclass="lang"></span><preclass="shiki shiki-themes github-light github-dark vp-code"><code><spanclass="line"><span>name = 'World'</span></span>
<spanclass="line"><span>print('Hello, %s!' % (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><h1id="comprehensions-generator-expressions"tabindex="-1">Comprehensions & Generator Expressions <aclass="header-anchor"href="#comprehensions-generator-expressions"aria-label="Permalink to "Comprehensions & Generator Expressions""></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><h1id="lambdas"tabindex="-1">Lambdas <aclass="header-anchor"href="#lambdas"aria-label="Permalink to "Lambdas""></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><h1id="conditional-expressions"tabindex="-1">Conditional Expressions <aclass="header-anchor"href="#conditional-expressions"aria-label="Permalink to "Conditional Expressions""></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><divclass="language- vp-adaptive-theme"><buttontitle="Copy Code"class="copy"></button><spanclass="lang"></span><preclass="shiki shiki-themes github-light github-dark vp-code"><code><spanclass="line"><span>x = 1 if cond else 2</span></span></code></pre></div><p>It's generally not a good idea to use these as function arguments, sequence items, etc. It's too easy to overlook.</p><h1id="default-argument-values"tabindex="-1">Default Argument Values <aclass="header-anchor"href="#default-argument-values"aria-label="Permalink to "Default Argument Values""></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'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><divclass="language- vp-adaptive-theme"><buttontitle="Copy Code"class="copy"></button><spanclass="lang"></span><preclass="shiki shiki-themes github-light github-dark vp-code"><code><spanclass="line"><span>def my_func(foo={}):</span></span>
<spanclass="line"><span> self._bar = bar</span></span></code></pre></div><h1id="true-false-evaluations"tabindex="-1">True/False Evaluations <aclass="header-anchor"href="#true-false-evaluations"aria-label="Permalink to "True/False Evaluations""></a></h1><p>You should generally prefer the implicit True/False evaluation in if statements, rather than checking equivalency.</p><p>Bad:</p><divclass="language- vp-adaptive-theme"><buttontitle="Copy Code"class="copy"></button><spanclass="lang"></span><preclass="shiki shiki-themes github-light github-dark vp-code"><code><spanclass="line"><span>if foo == True:</span></span>
<spanclass="line"><span> pass</span></span>
<spanclass="line"><span></span></span>
<spanclass="line"><span>if bar == False:</span></span>
<spanclass="line"><span> pass</span></span></code></pre></div><h1id="decorators"tabindex="-1">Decorators <aclass="header-anchor"href="#decorators"aria-label="Permalink to "Decorators""></a></h1><p>Use when appropriate. Try to avoid too much magic unless it helps with understanding.</p><h1id="threading-and-multiprocessing"tabindex="-1">Threading and Multiprocessing <aclass="header-anchor"href="#threading-and-multiprocessing"aria-label="Permalink to "Threading and Multiprocessing""></a></h1><p>Should be avoided. If you need this you will have to make a strong case before we merge your code.</p><h1id="power-features"tabindex="-1">Power Features <aclass="header-anchor"href="#power-features"aria-label="Permalink to "Power Features""></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'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><h1id="type-annotated-code"tabindex="-1">Type Annotated Code <aclass="header-anchor"href="#type-annotated-code"aria-label="Permalink to "Type Annotated Code""></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><h1id="function-length"tabindex="-1">Function length <aclass="header-anchor"href="#function-length"aria-label="Permalink to "Function length""></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><h1id="fixmes"tabindex="-1">FIXMEs <aclass="header-anchor"href="#fixmes"aria-label="Permalink to "FIXMEs""></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><divclass="language- vp-adaptive-theme"><buttontitle="Copy Code"class="copy"></button><spanclass="lang"></span><preclass="shiki shiki-themes github-light github-dark vp-code"><code><spanclass="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><h1id="testing"tabindex="-1">Testing <aclass="header-anchor"href="#testing"aria-label="Permalink to "Testing""></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