mirror of
https://github.com/google/styleguide.git
synced 2024-03-22 13:11:43 +08:00
Merge branch 'google:gh-pages' into gh-pages
This commit is contained in:
commit
888f2a9bcb
277
cppguide.html
277
cppguide.html
|
@ -21,8 +21,8 @@ this power brings with it complexity, which in turn can make
|
|||
code more bug-prone and harder to read and maintain.</p>
|
||||
|
||||
<p>The goal of this guide is to manage this complexity by
|
||||
describing in detail the dos and don'ts of writing C++ code
|
||||
. These rules exist to
|
||||
describing in detail the dos and don'ts of writing C++
|
||||
code. These rules exist to
|
||||
keep the code base manageable while still allowing
|
||||
coders to use C++ language features productively.</p>
|
||||
|
||||
|
@ -164,9 +164,8 @@ input.</p>
|
|||
|
||||
<h2 id="C++_Version">C++ Version</h2>
|
||||
|
||||
<p>Currently, code should target C++17, i.e., should not use C++2x
|
||||
features, with the exception of <a href="#Designated_initializers">designated
|
||||
initializers</a>. The C++ version targeted by this guide will advance
|
||||
<p>Currently, code should target C++20, i.e., should not use C++23
|
||||
features. The C++ version targeted by this guide will advance
|
||||
(aggressively) over time.</p>
|
||||
|
||||
|
||||
|
@ -176,7 +175,7 @@ input.</p>
|
|||
|
||||
<div>
|
||||
<p>Consider portability to other environments before using features
|
||||
from C++14 and C++17 in your project.</p>
|
||||
from C++17 and C++20 in your project.</p>
|
||||
</div>
|
||||
|
||||
<h2 id="Header_Files">Header Files</h2>
|
||||
|
@ -415,6 +414,18 @@ should be included as:</p>
|
|||
<pre>#include "base/logging.h"
|
||||
</pre>
|
||||
|
||||
<p>Headers should only be included using an angle-bracketed path if the library
|
||||
requires you to do so. In particular, the following headers require angle
|
||||
brackets:</p>
|
||||
|
||||
<ul>
|
||||
<li>C and C++ standard library headers (e.g. <code><stdlib.h></code>
|
||||
and <code><string></code>).</li>
|
||||
<li>POSIX, Linux, and Windows system headers (e.g. <code><unistd.h></code>
|
||||
and <code><windows.h></code>).</li>
|
||||
<li>In rare cases, third_party libraries (e.g. <code><Python.h></code>).</li>
|
||||
</ul>
|
||||
|
||||
<p>In <code><var>dir/foo</var>.cc</code> or
|
||||
<code><var>dir/foo_test</var>.cc</code>, whose main
|
||||
purpose is to implement or test the stuff in
|
||||
|
@ -426,9 +437,9 @@ as follows:</p>
|
|||
|
||||
<li>A blank line</li>
|
||||
|
||||
<li>C system headers (more precisely: headers in angle brackets with the
|
||||
<code>.h</code> extension), e.g., <code><unistd.h></code>,
|
||||
<code><stdlib.h></code>.</li>
|
||||
<li>C system headers, and any other headers in angle brackets with the
|
||||
<code>.h</code> extension, e.g., <code><unistd.h></code>,
|
||||
<code><stdlib.h></code>, <code><Python.h></code>.</li>
|
||||
|
||||
<li>A blank line</li>
|
||||
|
||||
|
@ -664,9 +675,9 @@ namespace baz = ::foo::bar::baz;
|
|||
|
||||
<pre>// Shorten access to some commonly used names (in a .h file).
|
||||
namespace librarian {
|
||||
namespace impl { // Internal, not part of the API.
|
||||
namespace internal { // Internal, not part of the API.
|
||||
namespace sidetable = ::pipeline_diagnostics::sidetable;
|
||||
} // namespace impl
|
||||
} // namespace internal
|
||||
|
||||
inline void my_inline_function() {
|
||||
// namespace alias local to a function (or method).
|
||||
|
@ -686,6 +697,11 @@ inline void my_inline_function() {
|
|||
using ::absl::container_internal::ImplementationDetail;
|
||||
</pre>
|
||||
</li>
|
||||
|
||||
<li><p>Single-line nested namespace declarations
|
||||
|
||||
are preferred in new code, but are not required.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<a id="Unnamed_Namespaces_and_Static_Variables"></a>
|
||||
|
@ -932,7 +948,7 @@ the formal language of the C++ standard. It means that the initializing
|
|||
expression is a constant expression, and if the object is initialized by a
|
||||
constructor call, then the constructor must be specified as
|
||||
<code>constexpr</code>, too:</p>
|
||||
<pre>struct Foo { constexpr Foo(int) {} };
|
||||
<pre class="goodcode">struct Foo { constexpr Foo(int) {} };
|
||||
|
||||
int n = 5; // Fine, 5 is a constant expression.
|
||||
Foo x(2); // Fine, 2 is a constant expression and the chosen constructor is constexpr.
|
||||
|
@ -940,12 +956,8 @@ Foo a[] = { Foo(1), Foo(2), Foo(3) }; // Fine</pre>
|
|||
|
||||
<p>Constant initialization is always allowed. Constant initialization of
|
||||
static storage duration variables should be marked with <code>constexpr</code>
|
||||
or where possible the
|
||||
|
||||
|
||||
<a href="https://github.com/abseil/abseil-cpp/blob/03c1513538584f4a04d666be5eb469e3979febba/absl/base/attributes.h#L540">
|
||||
<code>ABSL_CONST_INIT</code></a>
|
||||
attribute. Any non-local static storage
|
||||
or <code>constinit</code>.
|
||||
Any non-local static storage
|
||||
duration variable that is not so marked should be presumed to have
|
||||
dynamic initialization, and reviewed very carefully.</p>
|
||||
|
||||
|
@ -1017,10 +1029,8 @@ does not make an observable difference. For example:</p>
|
|||
<p><code>thread_local</code> variables that aren't declared inside a function
|
||||
must be initialized with a true compile-time constant,
|
||||
and this must be enforced by using the
|
||||
|
||||
|
||||
<a href="https://github.com/abseil/abseil-cpp/blob/master/absl/base/attributes.h">
|
||||
<code>ABSL_CONST_INIT</code></a>
|
||||
<a href="https://en.cppreference.com/w/cpp/language/constinit">
|
||||
<code>constinit</code></a>
|
||||
attribute. Prefer
|
||||
<code>thread_local</code> over other ways of defining thread-local data.</p>
|
||||
|
||||
|
@ -1093,13 +1103,11 @@ get a particularly hard to diagnose use-after-free.</p>
|
|||
initialized with a true compile-time constant (i.e., they must have no
|
||||
dynamic initialization). To enforce this, <code>thread_local</code> variables
|
||||
at class or namespace scope must be annotated with
|
||||
|
||||
|
||||
<a href="https://github.com/abseil/abseil-cpp/blob/master/absl/base/attributes.h">
|
||||
<code>ABSL_CONST_INIT</code></a>
|
||||
<a href="https://en.cppreference.com/w/cpp/language/constinit">
|
||||
<code>constinit</code></a>
|
||||
(or <code>constexpr</code>, but that should be rare):</p>
|
||||
|
||||
<pre> ABSL_CONST_INIT thread_local Foo foo = ...;
|
||||
<pre> constinit thread_local Foo foo = ...;
|
||||
</pre>
|
||||
|
||||
<p><code>thread_local</code> variables inside a function have no initialization
|
||||
|
@ -1177,8 +1185,7 @@ for your code ,
|
|||
terminating the program may be an appropriate error handling
|
||||
response. Otherwise, consider a factory function
|
||||
or <code>Init()</code> method as described in
|
||||
<a href="https://abseil.io/tips/42">TotW #42</a>
|
||||
.
|
||||
<a href="https://abseil.io/tips/42">TotW #42</a>.
|
||||
Avoid <code>Init()</code> methods on objects with
|
||||
no other states that affect which public methods may be called
|
||||
(semi-constructed objects of this form are particularly hard to work
|
||||
|
@ -1445,8 +1452,6 @@ by making their constructors protected, by declaring their destructors protected
|
|||
or by giving them one or more pure virtual member functions. Prefer to avoid
|
||||
deriving from concrete classes.</p>
|
||||
|
||||
|
||||
|
||||
<h3 id="Structs_vs._Classes">Structs vs. Classes</h3>
|
||||
|
||||
<p>Use a <code>struct</code> only for passive objects that
|
||||
|
@ -1466,9 +1471,9 @@ break those invariants. Constructors, destructors, and helper methods may
|
|||
be present; however, these methods must not require or enforce any
|
||||
invariants.</p>
|
||||
|
||||
<p>If more functionality or invariants are required, a
|
||||
<code>class</code> is more appropriate. If in doubt, make
|
||||
it a <code>class</code>.</p>
|
||||
<p>If more functionality or invariants are required, or struct has wide visibility and expected to
|
||||
evolve, then a <code>class</code> is more appropriate. If in doubt, make it a <code>class</code>.
|
||||
</p>
|
||||
|
||||
<p>For consistency with STL, you can use
|
||||
<code>struct</code> instead of <code>class</code> for
|
||||
|
@ -1678,17 +1683,23 @@ definitions. If possible, avoid defining operators as templates,
|
|||
because they must satisfy this rule for any possible template
|
||||
arguments. If you define an operator, also define
|
||||
any related operators that make sense, and make sure they
|
||||
are defined consistently. For example, if you overload
|
||||
<code><</code>, overload all the comparison operators,
|
||||
and make sure <code><</code> and <code>></code> never
|
||||
return true for the same arguments.</p>
|
||||
are defined consistently.</p>
|
||||
|
||||
<p>Prefer to define non-modifying binary operators as
|
||||
non-member functions. If a binary operator is defined as a
|
||||
class member, implicit conversions will apply to the
|
||||
right-hand argument, but not the left-hand one. It will
|
||||
confuse your users if <code>a < b</code> compiles but
|
||||
<code>b < a</code> doesn't.</p>
|
||||
confuse your users if <code>a + b</code> compiles but
|
||||
<code>b + a</code> doesn't.</p>
|
||||
|
||||
<p>For a type <code>T</code> whose values can be compared for
|
||||
equality, define a non-member <code>operator==</code> and document when
|
||||
two values of type <code>T</code> are considered equal.
|
||||
If there is a single obvious notion of when a value <code>t1</code>
|
||||
of type <code>T</code> is less than another such value <code>t2</code> then
|
||||
you may also define <code>operator<=></code>, which should be
|
||||
consistent with <code>operator==</code>.
|
||||
Prefer not to overload the other comparison and ordering operators.</p>
|
||||
|
||||
<p>Don't go out of your way to avoid defining operator
|
||||
overloads. For example, prefer to define <code>==</code>,
|
||||
|
@ -1788,7 +1799,7 @@ improve readability, and often provide the same or better
|
|||
performance.</p>
|
||||
|
||||
<p>Prefer to return by value or, failing that, return by reference.
|
||||
Avoid returning a pointer unless it can be null.</p>
|
||||
Avoid returning a raw pointer unless it can be null.</p>
|
||||
|
||||
<p>Parameters are either inputs to the function, outputs from the
|
||||
function, or both. Non-optional input parameters should usually be values
|
||||
|
@ -1802,10 +1813,10 @@ optional outputs and optional input/output parameters.</p>
|
|||
|
||||
|
||||
<p>
|
||||
Avoid defining functions that require a <code>const</code> reference parameter
|
||||
to outlive the call, because <code>const</code> reference parameters bind
|
||||
to temporaries. Instead, find a way to eliminate the lifetime requirement
|
||||
(for example, by copying the parameter), or pass it by <code>const</code>
|
||||
Avoid defining functions that require a reference parameter to outlive the call.
|
||||
In some cases reference parameters can bind to temporaries, leading to lifetime
|
||||
bugs. Instead, find a way to eliminate the lifetime requirement
|
||||
(for example, by copying the parameter), or pass retained parameters by
|
||||
pointer and document the lifetime and non-null requirements.
|
||||
|
||||
</p>
|
||||
|
@ -2249,10 +2260,10 @@ qualifier to methods), except as follows:</p>
|
|||
<li>You may use them to define pairs of overloads, such as one taking
|
||||
<code>Foo&&</code> and the other taking <code>const Foo&</code>.
|
||||
Usually the preferred solution is just to pass by value, but an overloaded
|
||||
pair of functions sometimes yields better performance and is sometimes
|
||||
necessary in generic code that needs to support a wide variety of types.
|
||||
As always: if you're writing more complicated code for the sake of
|
||||
performance, make sure you have evidence that it actually helps.</li>
|
||||
pair of functions sometimes yields better performance, for example if the
|
||||
functions sometimes don't consume the input. As always: if you're writing
|
||||
more complicated code for the sake of performance, make sure you have evidence
|
||||
that it actually helps.</li>
|
||||
</ul>
|
||||
|
||||
<h3 id="Friends">Friends</h3>
|
||||
|
@ -2636,9 +2647,9 @@ casts when explicit type conversion is necessary.
|
|||
including <code>void*</code>. Use this
|
||||
only if you know what you are doing and you understand the aliasing
|
||||
issues. Also, consider dereferencing the pointer (without a cast) and
|
||||
using <code>absl::bit_cast</code> to cast the resulting value.</li>
|
||||
using <code>std::bit_cast</code> to cast the resulting value.</li>
|
||||
|
||||
<li>Use <code>absl::bit_cast</code> to interpret the raw bits of a
|
||||
<li>Use <code>std::bit_cast</code> to interpret the raw bits of a
|
||||
value using a different type of the same size (a type pun), such as
|
||||
interpreting the bits of a <code>double</code> as
|
||||
<code>int64_t</code>.</li>
|
||||
|
@ -2874,10 +2885,13 @@ putting the "adjective" (<code>const</code>) before the
|
|||
<code>const</code> first, we do not require it. But be
|
||||
consistent with the code around you!</p>
|
||||
|
||||
<h3 id="Use_of_constexpr">Use of constexpr</h3>
|
||||
<h3 id="Use_of_constexpr">Use of constexpr, constinit, and consteval</h3>
|
||||
|
||||
<p>Use <code>constexpr</code> to define true
|
||||
constants or to ensure constant initialization.</p>
|
||||
constants or to ensure constant initialization.
|
||||
Use <code>constinit</code> to ensure constant
|
||||
initialization for non-constant variables.
|
||||
</p>
|
||||
|
||||
<p class="definition"></p>
|
||||
<p> Some variables can be declared <code>constexpr</code>
|
||||
|
@ -2885,7 +2899,8 @@ to indicate the variables are true constants, i.e., fixed at
|
|||
compilation/link time. Some functions and constructors
|
||||
can be declared <code>constexpr</code> which enables them
|
||||
to be used in defining a <code>constexpr</code>
|
||||
variable.</p>
|
||||
variable. Functions can be declared <code>consteval</code>
|
||||
to restrict their use to compile time.</p>
|
||||
|
||||
<p class="pros"></p>
|
||||
<p>Use of <code>constexpr</code> enables definition of
|
||||
|
@ -2906,9 +2921,11 @@ in these definitions.</p>
|
|||
robust specification of the constant parts of an
|
||||
interface. Use <code>constexpr</code> to specify true
|
||||
constants and the functions that support their
|
||||
definitions. Avoid complexifying function definitions to
|
||||
definitions. <code>consteval</code> may be used for
|
||||
code that must not be invoked at runtime.
|
||||
Avoid complexifying function definitions to
|
||||
enable their use with <code>constexpr</code>. Do not use
|
||||
<code>constexpr</code> to force inlining.</p>
|
||||
<code>constexpr</code> or <code>consteval</code> to force inlining.</p>
|
||||
|
||||
<h3 id="Integer_Types">Integer Types</h3>
|
||||
|
||||
|
@ -2948,7 +2965,9 @@ like <code>int16_t</code>, <code>uint32_t</code>,
|
|||
<code>int64_t</code>, etc. You should always use
|
||||
those in preference to <code>short</code>, <code>unsigned
|
||||
long long</code> and the like, when you need a guarantee
|
||||
on the size of an integer. Of the built-in integer types, only
|
||||
on the size of an integer. Prefer to omit the <code>std::</code>
|
||||
prefix for these types, as the extra 5 characters do
|
||||
not merit the added clutter. Of the built-in integer types, only
|
||||
<code>int</code> should be used. When appropriate, you
|
||||
are welcome to use standard type aliases like
|
||||
<code>size_t</code> and <code>ptrdiff_t</code>.</p>
|
||||
|
@ -3148,7 +3167,6 @@ possible:</p>
|
|||
<li>Prefer not using <code>##</code> to generate
|
||||
function/class/variable names.</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
<p>Exporting macros from headers (i.e., defining them in a header
|
||||
|
@ -3230,8 +3248,8 @@ auto c = b; // c is an int
|
|||
auto d{42}; // d is an int, not a std::initializer_list<int>
|
||||
</pre>
|
||||
<code>auto</code> can be qualified with <code>const</code>, and can be
|
||||
used as part of a pointer or reference type, but it can't be used as a
|
||||
template argument. A rare variant of this syntax uses
|
||||
used as part of a pointer or reference type, and (since C++17) as a
|
||||
non-type template argument. A rare variant of this syntax uses
|
||||
<code>decltype(auto)</code> instead of <code>auto</code>, in which case
|
||||
the deduced type is the result of applying
|
||||
<a href="https://en.cppreference.com/w/cpp/language/decltype"><code>decltype</code></a>
|
||||
|
@ -3525,8 +3543,8 @@ ordering of fields than the <code>Point</code> example above.</p>
|
|||
|
||||
<p class="cons"></p>
|
||||
<p>While designated initializers have long been part of the C standard and
|
||||
supported by C++ compilers as an extension, only recently have they made it
|
||||
into the C++ standard, being added as part of C++20.</p>
|
||||
supported by C++ compilers as an extension, they were not supported by
|
||||
C++ prior to C++20.</p>
|
||||
|
||||
<p>The rules in the C++ standard are stricter than in C and compiler extensions,
|
||||
requiring that the designated initializers appear in the same order as the
|
||||
|
@ -3768,6 +3786,113 @@ error messages are part of your user interface, and your code should
|
|||
be tweaked as necessary so that the error messages are understandable
|
||||
and actionable from a user point of view.</p>
|
||||
|
||||
<h3 id="Concepts">Concepts and Constraints</h3>
|
||||
|
||||
<p>Use concepts sparingly.
|
||||
In general, concepts and constraints should only be used in cases
|
||||
where templates would have been used prior to C++20.
|
||||
Avoid introducing new concepts in headers,
|
||||
unless the headers are marked as internal to the library.
|
||||
Do not define concepts that are not enforced by the compiler.
|
||||
Prefer constraints over template metaprogramming, and
|
||||
avoid the <code>template<<i>Concept</i> T></code> syntax;
|
||||
instead, use the <code>requires(<i>Concept<T></i>)</code>
|
||||
syntax.</p>
|
||||
|
||||
<p class="definition"></p>
|
||||
<p>The <code>concept</code> keyword is a new mechanism for defining
|
||||
requirements (such as type traits or interface specifications)
|
||||
for a template parameter.
|
||||
The <code>requires</code> keyword provides mechanisms for placing
|
||||
anonymous constraints on templates and verifying that constraints
|
||||
are satisfied at compile time.
|
||||
Concepts and constraints are often used together, but can be
|
||||
also used independently.</p>
|
||||
|
||||
<p class="pros"></p>
|
||||
<ul>
|
||||
<li>Concepts allow the compiler to generate much better error
|
||||
messages when templates are involved, which can reduce confusion
|
||||
and significantly improve the development experience.</li>
|
||||
<li>Concepts can reduce the boilerplate necessary for defining
|
||||
and using compile-time constraints, often increasing the clarity
|
||||
of the resulting code.</li>
|
||||
<li>Constraints provide some capabilities that are difficult to
|
||||
achieve with templates and SFINAE techniques.</li>
|
||||
</ul>
|
||||
|
||||
<p class="cons"></p>
|
||||
<ul>
|
||||
<li>As with templates, concepts can make code significantly more
|
||||
complex and difficult to understand.</li>
|
||||
<li>Concept syntax can be confusing to readers, as concepts
|
||||
appear similar to class types at their usage sites.</li>
|
||||
<li>Concepts, especially at API boundaries, increase code
|
||||
coupling, rigidity, and ossification.</li>
|
||||
<li>Concepts and constraints can replicate logic from a function
|
||||
body, resulting in code duplication and increased maintenance
|
||||
costs.</li>
|
||||
<li>Concepts muddy the source of truth for their underlying
|
||||
contracts, as they are standalone named entities that can be
|
||||
utilized in multiple locations, all of which evolve separately
|
||||
from each other.
|
||||
This can cause the stated and implied requirements to diverge
|
||||
over time.</li>
|
||||
<li>Concepts and constraints affect overload resolution in novel
|
||||
and non-obvious ways.</li>
|
||||
<li>As with SFINAE, constraints make it harder to refactor code
|
||||
at scale.</li>
|
||||
</ul>
|
||||
|
||||
<p class="decision"></p>
|
||||
<p>Predefined concepts in the standard library should be
|
||||
preferred to type traits, when equivalent ones exist.
|
||||
(e.g., if <code>std::is_integral_v</code> would have been used
|
||||
before C++20, then <code>std::integral</code> should be used in
|
||||
C++20 code.)
|
||||
Similarly, prefer modern constraint syntax
|
||||
(via <code>requires(<i>Condition</i>)</code>).
|
||||
Avoid legacy template metaprogramming constructs
|
||||
(such as <code>std::enable_if<<i>Condition</i>></code>)
|
||||
as well as the <code>template<<i>Concept</i> T></code>
|
||||
syntax.</p>
|
||||
|
||||
<p>Do not manually re-implement any existing concepts or traits.
|
||||
For example, use
|
||||
<code>requires(std::default_initializable<T>)</code>
|
||||
instead of
|
||||
<code>requires(requires { T v; })</code>
|
||||
or the like.
|
||||
|
||||
</p><p>New <code>concept</code> declarations should be rare, and only
|
||||
defined internally within a library, such that they are not
|
||||
exposed at API boundaries.
|
||||
More generally, do not use concepts or constraints in cases where
|
||||
you wouldn't use their legacy template equivalents in C++17.
|
||||
</p>
|
||||
|
||||
<p>Do not define concepts that duplicate the function body,
|
||||
or impose requirements that would be insignificant or obvious
|
||||
from reading the body of the code or the resulting error messages.
|
||||
For example, avoid the following:
|
||||
</p><pre class="badcode">template <typename T> // Bad - redundant with negligible benefit
|
||||
concept Addable = std::copyable<T> && requires(T a, T b) { a + b; };
|
||||
template <Addable T>
|
||||
T Add(T x, T y, T z) { return x + y + z; }
|
||||
</pre>
|
||||
Instead, prefer to leave code as an ordinary template unless
|
||||
you can demonstrate that concepts result in significant
|
||||
improvement for that particular case, such as in the resulting
|
||||
error messages for a deeply nested or non-obvious
|
||||
requirement.
|
||||
|
||||
<p>Concepts should be statically verifiable by the compiler.
|
||||
Do not use any concept whose primary benefits would come from a
|
||||
semantic (or otherwise unenforced) constraint.
|
||||
Requirements that are unenforced at compile time should instead
|
||||
be imposed via other mechanisms such as comments, assertions,
|
||||
or tests.</p>
|
||||
|
||||
<h3 id="Boost">Boost</h3>
|
||||
|
||||
<p>Use only approved libraries from the Boost library
|
||||
|
@ -3955,9 +4080,10 @@ guide, the following C++ features may not be used:</p>
|
|||
|
||||
<p class="definition"></p>
|
||||
<p>There are several ways to create names that are aliases of other entities:</p>
|
||||
<pre>typedef Foo Bar;
|
||||
using Bar = Foo;
|
||||
using other_namespace::Foo;
|
||||
<pre>using Bar = Foo;
|
||||
typedef Foo Bar; // But prefer `using` in C++ code.
|
||||
using ::other_namespace::Foo;
|
||||
using enum MyEnumType; // Creates aliases for all enumerators in MyEnumType.
|
||||
</pre>
|
||||
|
||||
<p>In new code, <code>using</code> is preferable to <code>typedef</code>,
|
||||
|
@ -4245,6 +4371,10 @@ using PropertiesMap = hash_map<UrlTableProperties *, std::string>;
|
|||
enum class UrlTableError { ...
|
||||
</pre>
|
||||
|
||||
<h3 id="Concept_Names">Concept Names</h3>
|
||||
|
||||
Concept names follow the same rules as <a href="#Type_Names">type names</a>.
|
||||
|
||||
<h3 id="Variable_Names">Variable Names</h3>
|
||||
|
||||
<p>The names of variables (including function parameters) and data members are
|
||||
|
@ -4308,9 +4438,10 @@ const int kAndroid8_0_0 = 24; // Android 8.0.0
|
|||
|
||||
<p>All such variables with static storage duration (i.e., statics and globals,
|
||||
see <a href="http://en.cppreference.com/w/cpp/language/storage_duration#Storage_duration">
|
||||
Storage Duration</a> for details) should be named this way. This
|
||||
convention is optional for variables of other storage classes, e.g., automatic
|
||||
variables; otherwise the usual variable naming rules apply. For example:</p>
|
||||
Storage Duration</a> for details) should be named this way, including those in templates where
|
||||
different instantiations of the template may have different values. This convention is optional for
|
||||
variables of other storage classes, e.g., automatic variables; otherwise the usual variable naming
|
||||
rules apply. For example:</p>
|
||||
|
||||
<pre>void ComputeFoo(absl::string_view suffix) {
|
||||
// Either of these is acceptable.
|
||||
|
@ -4515,7 +4646,7 @@ author line, consider deleting the author line.
|
|||
New files should usually not contain copyright notice or
|
||||
author line.</p>
|
||||
|
||||
<h3 id="Class_Comments">Class Comments</h3>
|
||||
<h3 id="Class_Comments">Struct and Class Comments</h3>
|
||||
|
||||
<p>Every non-obvious class or struct declaration should have an
|
||||
accompanying comment that describes what it is for and how it should
|
||||
|
@ -4532,6 +4663,8 @@ class GargantuanTableIterator {
|
|||
};
|
||||
</pre>
|
||||
|
||||
<h4 id="Class_Comments_Details">Class Comments</h4>
|
||||
|
||||
<p>The class comment should provide the reader with enough information to know
|
||||
how and when to use the class, as well as any additional considerations
|
||||
necessary to correctly use the class. Document the synchronization assumptions
|
||||
|
@ -4925,7 +5058,8 @@ if included in the source as straight UTF-8.</p>
|
|||
<p>When possible, avoid the <code>u8</code> prefix.
|
||||
It has significantly different semantics starting in C++20
|
||||
than in C++17, producing arrays of <code>char8_t</code>
|
||||
rather than <code>char</code>.
|
||||
rather than <code>char</code>, and will change again in C++23.
|
||||
|
||||
|
||||
</p><p>You shouldn't use <code>char16_t</code> and
|
||||
<code>char32_t</code> character types, since they're for
|
||||
|
@ -5091,7 +5225,8 @@ double d = 1248e6;
|
|||
</pre>
|
||||
|
||||
<pre class="goodcode">float f = 1.0f;
|
||||
float f2 = 1; // Also OK
|
||||
float f2 = 1.0; // Also OK
|
||||
float f3 = 1; // Also OK
|
||||
long double ld = -0.5L;
|
||||
double d = 1248.0e6;
|
||||
</pre>
|
||||
|
|
|
@ -597,7 +597,11 @@ to have them in the same package.
|
|||
Code within a package can access unexported identifiers in the package. If you
|
||||
have a few related types whose *implementation* is tightly coupled, placing them
|
||||
in the same package lets you achieve this coupling without polluting the public
|
||||
API with these details.
|
||||
API with these details. A good test for this coupling is to imagine a
|
||||
hypothetical user of two packages, where the packages cover closely related
|
||||
topics: if the user must import both packages in order to use either in any
|
||||
meaningful way, combining them together is usually the right thing to do. The
|
||||
standard library generally demonstrates this kind of scoping and layering well.
|
||||
|
||||
All of that being said, putting your entire project in a single package would
|
||||
likely make that package too large. When something is conceptually distinct,
|
||||
|
@ -776,7 +780,7 @@ var (
|
|||
ErrMarsupial = errors.New("marsupials are not supported")
|
||||
)
|
||||
|
||||
func pet(animal Animal) error {
|
||||
func process(animal Animal) error {
|
||||
switch {
|
||||
case seen[animal]:
|
||||
return ErrDuplicate
|
||||
|
@ -849,6 +853,8 @@ to know if using status codes is the right choice.
|
|||
|
||||
[`os.PathError`]: https://pkg.go.dev/os#PathError
|
||||
[`errors.Is`]: https://pkg.go.dev/errors#Is
|
||||
[`errors.As`]: https://pkg.go.dev/errors#As
|
||||
[`package cmp`]: https://pkg.go.dev/github.com/google/go-cmp/cmp
|
||||
[status]: https://pkg.go.dev/google.golang.org/grpc/status
|
||||
[canonical codes]: https://pkg.go.dev/google.golang.org/grpc/codes
|
||||
|
||||
|
@ -978,6 +984,10 @@ func (*FortuneTeller) SuggestFortune(context.Context, *pb.SuggestionRequest) (*p
|
|||
}
|
||||
```
|
||||
|
||||
See also:
|
||||
|
||||
* [Error Documentation Conventions](#documentation-conventions-errors)
|
||||
|
||||
<a id="error-percent-w"></a>
|
||||
|
||||
### Placement of %w in errors
|
||||
|
@ -1255,7 +1265,7 @@ information to the reader:
|
|||
// string.
|
||||
//
|
||||
// format is the format, and data is the interpolation data.
|
||||
func Sprintf(format string, data ...interface{}) string
|
||||
func Sprintf(format string, data ...any) string
|
||||
```
|
||||
|
||||
However, this snippet demonstrates a code scenario similar to the previous where
|
||||
|
@ -1272,7 +1282,7 @@ reader:
|
|||
// the format specification, the function will inline warnings about formatting
|
||||
// errors into the output string as described by the Format errors section
|
||||
// above.
|
||||
func Sprintf(format string, data ...interface{}) string
|
||||
func Sprintf(format string, data ...any) string
|
||||
```
|
||||
|
||||
Consider your likely audience in choosing what to document and at what depth.
|
||||
|
@ -1317,9 +1327,9 @@ func (Worker) Run(ctx context.Context) error
|
|||
```
|
||||
|
||||
Where context behavior is different or non-obvious, it should be expressly
|
||||
documented:
|
||||
documented if any of the following are true.
|
||||
|
||||
* If the function returns an error other than `ctx.Err()` when the context is
|
||||
* The function returns an error other than `ctx.Err()` when the context is
|
||||
cancelled:
|
||||
|
||||
```go
|
||||
|
@ -1330,8 +1340,7 @@ documented:
|
|||
func (Worker) Run(ctx context.Context) error
|
||||
```
|
||||
|
||||
* If the function has other mechanisms that may interrupt it or affect
|
||||
lifetime:
|
||||
* The function has other mechanisms that may interrupt it or affect lifetime:
|
||||
|
||||
```go
|
||||
// Good:
|
||||
|
@ -1347,7 +1356,7 @@ documented:
|
|||
func (Worker) Stop()
|
||||
```
|
||||
|
||||
* If the function has special expectations about context lifetime, lineage, or
|
||||
* The function has special expectations about context lifetime, lineage, or
|
||||
attached values:
|
||||
|
||||
```go
|
||||
|
@ -1394,9 +1403,9 @@ Similarly, the extra remark about concurrency can safely be removed here:
|
|||
func (*Buffer) Grow(n int)
|
||||
```
|
||||
|
||||
Documentation is strongly encouraged if:
|
||||
Documentation is strongly encouraged if any of the following are true.
|
||||
|
||||
* it is unclear whether the operation is read-only or a mutating
|
||||
* It is unclear whether the operation is read-only or mutating:
|
||||
|
||||
```go
|
||||
// Good:
|
||||
|
@ -1411,7 +1420,7 @@ Documentation is strongly encouraged if:
|
|||
Why? A cache hit when looking up the key mutate a LRU cache internally. How
|
||||
this is implemented may not be obvious to all readers.
|
||||
|
||||
* synchronization is provided by API
|
||||
* Synchronization is provided by the API:
|
||||
|
||||
```go
|
||||
// Good:
|
||||
|
@ -1427,7 +1436,7 @@ Documentation is strongly encouraged if:
|
|||
**Note:** If the API is a type and the API provides synchronization in
|
||||
entirety, conventionally only the type definition documents the semantics.
|
||||
|
||||
* the API consumes user-implemented types of interfaces, and the interface's
|
||||
* The API consumes user-implemented types of interfaces, and the interface's
|
||||
consumer has particular concurrency requirements:
|
||||
|
||||
```go
|
||||
|
@ -1489,6 +1498,84 @@ If it is potentially unclear how to clean up the resources, explain how:
|
|||
func (c *Client) Get(url string) (resp *Response, err error)
|
||||
```
|
||||
|
||||
See also:
|
||||
|
||||
* [GoTip #110: Don’t Mix Exit With Defer]
|
||||
|
||||
[GoTip #110: Don’t Mix Exit With Defer]: https://google.github.io/styleguide/go/index.html#gotip
|
||||
|
||||
<a id="documentation-conventions-errors"></a>
|
||||
|
||||
#### Errors
|
||||
|
||||
Document significant error sentinel values or error types that your functions
|
||||
return to callers so that callers can anticipate what types of conditions they
|
||||
can handle in their code.
|
||||
|
||||
```go
|
||||
// Good:
|
||||
package os
|
||||
|
||||
// Read reads up to len(b) bytes from the File and stores them in b. It returns
|
||||
// the number of bytes read and any error encountered.
|
||||
//
|
||||
// At end of file, Read returns 0, io.EOF.
|
||||
func (*File) Read(b []byte) (n int, err error) {
|
||||
```
|
||||
|
||||
When a function returns a specific error type, correctly note whether the error
|
||||
is a pointer receiver or not:
|
||||
|
||||
```go
|
||||
// Good:
|
||||
package os
|
||||
|
||||
type PathError struct {
|
||||
Op string
|
||||
Path string
|
||||
Err error
|
||||
}
|
||||
|
||||
// Chdir changes the current working directory to the named directory.
|
||||
//
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Chdir(dir string) error {
|
||||
```
|
||||
|
||||
Documenting whether the values returned are pointer receivers enables callers to
|
||||
correctly compare the errors using [`errors.Is`], [`errors.As`], and
|
||||
[`package cmp`]. This is because a non-pointer value is not equivalent to a
|
||||
pointer value.
|
||||
|
||||
**Note:** In the `Chdir` example, the return type is written as `error` rather
|
||||
than `*PathError` due to
|
||||
[how nil interface values work](https://go.dev/doc/faq#nil_error).
|
||||
|
||||
Document overall error conventions in the
|
||||
[package's documentation](decisions#package-comments) when the behavior is
|
||||
applicable to most errors found in the package:
|
||||
|
||||
```go
|
||||
// Good:
|
||||
// Package os provides a platform-independent interface to operating system
|
||||
// functionality.
|
||||
//
|
||||
// Often, more information is available within the error. For example, if a
|
||||
// call that takes a file name fails, such as Open or Stat, the error will
|
||||
// include the failing file name when printed and will be of type *PathError,
|
||||
// which may be unpacked for more information.
|
||||
package os
|
||||
```
|
||||
|
||||
Thoughtful application of these approaches can add
|
||||
[extra information to errors](#error-extra-info) without much effort and help
|
||||
callers avoid adding redundant annotations.
|
||||
|
||||
See also:
|
||||
|
||||
* [Go Tip #106: Error Naming Conventions](https://google.github.io/styleguide/go/index.html#gotip)
|
||||
* [Go Tip #89: When to Use Canonical Status Codes as Errors](https://google.github.io/styleguide/go/index.html#gotip)
|
||||
|
||||
<a id="documentation-preview"></a>
|
||||
|
||||
### Preview
|
||||
|
@ -1944,7 +2031,7 @@ func foo(ctx context.Context) {
|
|||
}
|
||||
```
|
||||
|
||||
**Note**: [Contexts are never included in option structs](decisions#contexts).
|
||||
**Note:** [Contexts are never included in option structs](decisions#contexts).
|
||||
|
||||
This option is often preferred when some of the following apply:
|
||||
|
||||
|
@ -2411,7 +2498,7 @@ func ExerciseGame(t *testing.T, cfg *Config, p chess.Player) error {
|
|||
if cfg.Simulation == Modem {
|
||||
conn, err := modempool.Allocate()
|
||||
if err != nil {
|
||||
t.Fatalf("no modem for the opponent could be provisioned: %v", err)
|
||||
t.Fatalf("No modem for the opponent could be provisioned: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { modempool.Return(conn) })
|
||||
}
|
||||
|
@ -2437,7 +2524,7 @@ func TestAcceptance(t *testing.T) {
|
|||
player := deepblue.New()
|
||||
err := chesstest.ExerciseGame(t, chesstest.SimpleGame, player)
|
||||
if err != nil {
|
||||
t.Errorf("deepblue player failed acceptance test: %v", err)
|
||||
t.Errorf("Deep Blue player failed acceptance test: %v", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -2578,14 +2665,14 @@ func paint(color string) error {
|
|||
func badSetup(t *testing.T) {
|
||||
// This should call t.Helper, but doesn't.
|
||||
if err := paint("taupe"); err != nil {
|
||||
t.Fatalf("could not paint the house under test: %v", err) // line 15
|
||||
t.Fatalf("Could not paint the house under test: %v", err) // line 15
|
||||
}
|
||||
}
|
||||
|
||||
func mustGoodSetup(t *testing.T) {
|
||||
t.Helper()
|
||||
if err := paint("lilac"); err != nil {
|
||||
t.Fatalf("could not paint the house under test: %v", err)
|
||||
t.Fatalf("Could not paint the house under test: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2605,10 +2692,10 @@ differs:
|
|||
|
||||
```text
|
||||
=== RUN TestBad
|
||||
paint_test.go:15: could not paint the house under test: no "taupe" paint today
|
||||
paint_test.go:15: Could not paint the house under test: no "taupe" paint today
|
||||
--- FAIL: TestBad (0.00s)
|
||||
=== RUN TestGood
|
||||
paint_test.go:32: could not paint the house under test: no "lilac" paint today
|
||||
paint_test.go:32: Could not paint the house under test: no "lilac" paint today
|
||||
--- FAIL: TestGood (0.00s)
|
||||
FAIL
|
||||
```
|
||||
|
@ -2616,7 +2703,7 @@ FAIL
|
|||
The error with `paint_test.go:15` refers to the line of the setup function that
|
||||
failed in `badSetup`:
|
||||
|
||||
`t.Fatalf("could not paint the house under test: %v", err)`
|
||||
`t.Fatalf("Could not paint the house under test: %v", err)`
|
||||
|
||||
Whereas `paint_test.go:32` refers to the line of the test that failed in
|
||||
`TestGood`:
|
||||
|
@ -2695,33 +2782,41 @@ and those should not depend on the system under test. Therefore, if a test
|
|||
helper [registers a fatal test failure](#test-helper-error-handling), it can and
|
||||
should do so from the test's goroutine.
|
||||
|
||||
<a id="t-field-names"></a>
|
||||
|
||||
### Use field names in struct literals
|
||||
|
||||
<a id="t-field-labels"></a>
|
||||
|
||||
### Use field labels for struct literals
|
||||
|
||||
In table-driven tests, prefer to specify the key for each test case specified.
|
||||
This is helpful when the test cases cover a large amount of vertical space (e.g.
|
||||
more than 20-30 lines), when there are adjacent fields with the same type, and
|
||||
also when you wish to omit fields which have the zero value. For example:
|
||||
In table-driven tests, prefer to specify field names when initializing test case
|
||||
struct literals. This is helpful when the test cases cover a large amount of
|
||||
vertical space (e.g. more than 20-30 lines), when there are adjacent fields with
|
||||
the same type, and also when you wish to omit fields which have the zero value.
|
||||
For example:
|
||||
|
||||
```go
|
||||
// Good:
|
||||
func TestStrJoin(t *testing.T) {
|
||||
tests := []struct {
|
||||
foo *pb.Foo
|
||||
bar *pb.Bar
|
||||
slice []string
|
||||
separator string
|
||||
skipEmpty bool
|
||||
want string
|
||||
}{
|
||||
{
|
||||
foo: pb.Foo_builder{
|
||||
Name: "foo",
|
||||
// ...
|
||||
}.Build(),
|
||||
bar: pb.Bar_builder{
|
||||
Name: "bar",
|
||||
// ...
|
||||
}.Build(),
|
||||
want: "result",
|
||||
slice: []string{"a", "b", ""},
|
||||
separator: ",",
|
||||
want: "a,b,",
|
||||
},
|
||||
{
|
||||
slice: []string{"a", "b", ""},
|
||||
separator: ",",
|
||||
skipEmpty: true,
|
||||
want: "a,b",
|
||||
},
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -2742,7 +2837,7 @@ func mustLoadDataset(t *testing.T) []byte {
|
|||
data, err := os.ReadFile("path/to/your/project/testdata/dataset")
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("could not load dataset: %v", err)
|
||||
t.Fatalf("Could not load dataset: %v", err)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
@ -2756,7 +2851,7 @@ func TestParseData(t *testing.T) {
|
|||
data := mustLoadDataset(t)
|
||||
parsed, err := ParseData(data)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error parsing data: %v", err)
|
||||
t.Fatalf("Unexpected error parsing data: %v", err)
|
||||
}
|
||||
want := &DataTable{ /* ... */ }
|
||||
if got := parsed; !cmp.Equal(got, want) {
|
||||
|
@ -2768,7 +2863,7 @@ func TestListContents(t *testing.T) {
|
|||
data := mustLoadDataset(t)
|
||||
contents, err := ListContents(data)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error listing contents: %v", err)
|
||||
t.Fatalf("Unexpected error listing contents: %v", err)
|
||||
}
|
||||
want := []string{ /* ... */ }
|
||||
if got := contents; !cmp.Equal(got, want) {
|
||||
|
@ -2916,7 +3011,7 @@ func mustLoadDataset(t *testing.T) []byte {
|
|||
dataset.err = err
|
||||
})
|
||||
if err := dataset.err; err != nil {
|
||||
t.Fatalf("could not load dataset: %v", err)
|
||||
t.Fatalf("Could not load dataset: %v", err)
|
||||
}
|
||||
return dataset.data
|
||||
}
|
||||
|
@ -2972,8 +3067,8 @@ guidance outlines when each method is preferred.
|
|||
|
||||
### Prefer "+" for simple cases
|
||||
|
||||
Prefer using "+" when concatenating few strings. This method is the
|
||||
syntactically the simplest and requires no import.
|
||||
Prefer using "+" when concatenating few strings. This method is syntactically
|
||||
the simplest and requires no import.
|
||||
|
||||
```go
|
||||
// Good:
|
||||
|
@ -3024,7 +3119,7 @@ for i, d := range digitsOfPi {
|
|||
str := b.String()
|
||||
```
|
||||
|
||||
**NOTE**: For more discussion, see
|
||||
**Note:** For more discussion, see
|
||||
[GoTip #29: Building Strings Efficiently](https://google.github.io/styleguide/go/index.html#gotip).
|
||||
|
||||
<a id="string-constants"></a>
|
||||
|
|
151
go/decisions.md
151
go/decisions.md
|
@ -897,9 +897,14 @@ import (
|
|||
)
|
||||
```
|
||||
|
||||
It is acceptable to split the project packages into multiple groups, for example
|
||||
if you want a separate group for renamed, imported-only-for-side-effects or
|
||||
another special group of imports.
|
||||
It is acceptable to split the project packages into multiple groups if you want
|
||||
a separate group, as long as the groups have some meaning. Common reasons to do
|
||||
this:
|
||||
|
||||
* renamed imports
|
||||
* packages imported for their side-effects
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
// Good:
|
||||
|
@ -1273,14 +1278,19 @@ maintainable.
|
|||
|
||||
#### Field names
|
||||
|
||||
Struct literals should usually specify **field names** for types defined outside
|
||||
the current package.
|
||||
Struct literals must specify **field names** for types defined outside the
|
||||
current package.
|
||||
|
||||
* Include field names for types from other packages.
|
||||
|
||||
```go
|
||||
// Good:
|
||||
good := otherpkg.Type{A: 42}
|
||||
// https://pkg.go.dev/encoding/csv#Reader
|
||||
r := csv.Reader{
|
||||
Comma: ',',
|
||||
Comment: '#',
|
||||
FieldsPerRecord: 4,
|
||||
}
|
||||
```
|
||||
|
||||
The position of fields in a struct and the full set of fields (both of which
|
||||
|
@ -1290,19 +1300,9 @@ the current package.
|
|||
|
||||
```go
|
||||
// Bad:
|
||||
// https://pkg.go.dev/encoding/csv#Reader
|
||||
r := csv.Reader{',', '#', 4, false, false, false, false}
|
||||
```
|
||||
|
||||
Field names may be omitted within small, simple structs whose composition
|
||||
and order are documented as being stable.
|
||||
|
||||
```go
|
||||
// Good:
|
||||
okay := image.Point{42, 54}
|
||||
also := image.Point{X: 42, Y: 54}
|
||||
```
|
||||
|
||||
* For package-local types, field names are optional.
|
||||
|
||||
```go
|
||||
|
@ -1721,33 +1721,6 @@ func (r *SomeType) SomeLongFunctionName(foo1, foo2, foo3 string,
|
|||
See [best practices](best-practices#funcargs) for a few options for shortening
|
||||
the call sites of functions that would otherwise have many arguments.
|
||||
|
||||
```go
|
||||
// Good:
|
||||
good := foo.Call(long, CallOptions{
|
||||
Names: list,
|
||||
Of: of,
|
||||
The: parameters,
|
||||
Func: all,
|
||||
Args: on,
|
||||
Now: separate,
|
||||
Visible: lines,
|
||||
})
|
||||
```
|
||||
|
||||
```go
|
||||
// Bad:
|
||||
bad := foo.Call(
|
||||
long,
|
||||
list,
|
||||
of,
|
||||
parameters,
|
||||
all,
|
||||
on,
|
||||
separate,
|
||||
lines,
|
||||
)
|
||||
```
|
||||
|
||||
Lines can often be shortened by factoring out local variables.
|
||||
|
||||
```go
|
||||
|
@ -1770,9 +1743,9 @@ bad := foo.Call(long, list, of, parameters,
|
|||
with, arbitrary, line, breaks)
|
||||
```
|
||||
|
||||
Do not add comments to specific function parameters. Instead, use an
|
||||
[option struct](best-practices#option-structure) or add more detail to the
|
||||
function documentation.
|
||||
Avoid adding inline comments to specific function arguments where possible.
|
||||
Instead, use an [option struct](best-practices#option-structure) or add more
|
||||
detail to the function documentation.
|
||||
|
||||
```go
|
||||
// Good:
|
||||
|
@ -1787,21 +1760,6 @@ bad := server.New(
|
|||
)
|
||||
```
|
||||
|
||||
If call-sites are uncomfortably long, consider refactoring:
|
||||
|
||||
```go
|
||||
// Good:
|
||||
// Sometimes variadic arguments can be factored out
|
||||
replacements := []string{
|
||||
"from", "to", // related values can be formatted adjacent to one another
|
||||
"source", "dest",
|
||||
"original", "new",
|
||||
}
|
||||
|
||||
// Use the replacement struct as inputs to NewReplacer.
|
||||
replacer := strings.NewReplacer(replacements...)
|
||||
```
|
||||
|
||||
If the API cannot be changed or if the local call is unusual (whether or not the
|
||||
call is too long), it is always permissible to add line breaks if it aids in
|
||||
understanding the call.
|
||||
|
@ -2110,7 +2068,7 @@ exclusively at
|
|||
func MustParse(version string) *Version {
|
||||
v, err := Parse(version)
|
||||
if err != nil {
|
||||
log.Fatalf("MustParse(%q) = _, %v", version, err)
|
||||
panic(fmt.Sprintf("MustParse(%q) = _, %v", version, err))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
@ -2120,8 +2078,6 @@ func MustParse(version string) *Version {
|
|||
var DefaultVersion = MustParse("1.2.3")
|
||||
```
|
||||
|
||||
**Note:** `log.Fatalf` is not the standard library log. See [#logging].
|
||||
|
||||
The same convention may be used in test helpers that only stop the current test
|
||||
(using `t.Fatal`). Such helpers are often convenient in creating test values,
|
||||
for example in struct fields of [table driven tests](#table-driven-tests), as
|
||||
|
@ -2133,7 +2089,7 @@ func mustMarshalAny(t *testing.T, m proto.Message) *anypb.Any {
|
|||
t.Helper()
|
||||
any, err := anypb.New(m)
|
||||
if err != nil {
|
||||
t.Fatalf("MustMarshalAny(t, m) = %v; want %v", err, nil)
|
||||
t.Fatalf("mustMarshalAny(t, m) = %v; want %v", err, nil)
|
||||
}
|
||||
return any
|
||||
}
|
||||
|
@ -2189,8 +2145,8 @@ func Version(o *servicepb.Object) (*version.Version, error) {
|
|||
When you spawn goroutines, make it clear when or whether they exit.
|
||||
|
||||
Goroutines can leak by blocking on channel sends or receives. The garbage
|
||||
collector will not terminate a goroutine even if the channels it is blocked on
|
||||
are unreachable.
|
||||
collector will not terminate a goroutine blocked on a channel even if no other
|
||||
goroutine has a reference to the channel.
|
||||
|
||||
Even when goroutines do not leak, leaving them in-flight when they are no longer
|
||||
needed can cause other subtle and hard-to-diagnose problems. Sending on a
|
||||
|
@ -2764,11 +2720,11 @@ See also:
|
|||
|
||||
### Logging
|
||||
|
||||
Go programs in the Google codebase use a variant of the
|
||||
[standard `log` package]. It has a similar but more powerful interface and
|
||||
interoperates well with internal Google systems. An open source version of this
|
||||
library is available as [package `glog`], and open source Google projects may
|
||||
use that, but this guide refers to it as `log` throughout.
|
||||
Go programs in the Google codebase use a variant of the standard [`log`]
|
||||
package. It has a similar but more powerful interface and interoperates well
|
||||
with internal Google systems. An open source version of this library is
|
||||
available as [package `glog`], and open source Google projects may use that, but
|
||||
this guide refers to it as `log` throughout.
|
||||
|
||||
**Note:** For abnormal program exits, this library uses `log.Fatal` to abort
|
||||
with a stacktrace, and `log.Exit` to stop without one. There is no `log.Panic`
|
||||
|
@ -2785,7 +2741,8 @@ See also:
|
|||
* When and how to use the log package to
|
||||
[stop the program](best-practices#checks-and-panics)
|
||||
|
||||
[standard `log` package]: https://pkg.go.dev/log
|
||||
[`log`]: https://pkg.go.dev/log
|
||||
[`log/slog`]: https://pkg.go.dev/log/slog
|
||||
[package `glog`]: https://pkg.go.dev/github.com/golang/glog
|
||||
[`log.Exit`]: https://pkg.go.dev/github.com/golang/glog#Exit
|
||||
[`log.Fatal`]: https://pkg.go.dev/github.com/golang/glog#Fatal
|
||||
|
@ -2978,15 +2935,15 @@ right:
|
|||
// Bad:
|
||||
package assert
|
||||
|
||||
func IsNotNil(t *testing.T, name string, val interface{}) {
|
||||
func IsNotNil(t *testing.T, name string, val any) {
|
||||
if val == nil {
|
||||
t.Fatalf("data %s = nil, want not nil", name)
|
||||
t.Fatalf("Data %s = nil, want not nil", name)
|
||||
}
|
||||
}
|
||||
|
||||
func StringEq(t *testing.T, name, got, want string) {
|
||||
if got != want {
|
||||
t.Fatalf("data %s = %q, want %q", name, got, want)
|
||||
t.Fatalf("Data %s = %q, want %q", name, got, want)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -3013,7 +2970,7 @@ want := BlogPost{
|
|||
}
|
||||
|
||||
if !cmp.Equal(got, want) {
|
||||
t.Errorf("blog post = %v, want = %v", got, want)
|
||||
t.Errorf("Blog post = %v, want = %v", got, want)
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -3029,7 +2986,7 @@ func TestBlogPost_VeritableRant(t *testing.T) {
|
|||
post := BlogPost{Body: "I am Gunnery Sergeant Hartman, your senior drill instructor."}
|
||||
|
||||
if got, want := postLength(post), 60; got != want {
|
||||
t.Errorf("length of post = %v, want %v", got, want)
|
||||
t.Errorf("Length of post = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -3361,7 +3318,8 @@ than relying on parsing the error message.
|
|||
Within unit tests, it is common to only care whether an error occurred or not.
|
||||
If so, then it is sufficient to only test whether the error was non-nil when you
|
||||
expected an error. If you would like to test that the error semantically matches
|
||||
some other error, then consider using `cmp` with [`cmpopts.EquateErrors`].
|
||||
some other error, then consider using [`errors.Is`] or `cmp` with
|
||||
[`cmpopts.EquateErrors`].
|
||||
|
||||
> **Note:** If a test uses [`cmpopts.EquateErrors`] but all of its `wantErr`
|
||||
> values are either `nil` or `cmpopts.AnyError`, then using `cmp` is
|
||||
|
@ -3370,9 +3328,10 @@ some other error, then consider using `cmp` with [`cmpopts.EquateErrors`].
|
|||
>
|
||||
> ```go
|
||||
> // Good:
|
||||
> gotErr := f(test.input) != nil
|
||||
> err := f(test.input)
|
||||
> gotErr := err != nil
|
||||
> if gotErr != test.wantErr {
|
||||
> t.Errorf("f(%q) returned err = %v, want error presence = %v", test.input, gotErr, test.wantErr)
|
||||
> t.Errorf("f(%q) = %v, want error presence = %v", test.input, err, test.wantErr)
|
||||
> }
|
||||
> ```
|
||||
|
||||
|
@ -3381,6 +3340,7 @@ See also
|
|||
|
||||
[tott-350]: https://testing.googleblog.com/2015/01/testing-on-toilet-change-detector-tests.html
|
||||
[`cmpopts.EquateErrors`]: https://pkg.go.dev/github.com/google/go-cmp/cmp/cmpopts#EquateErrors
|
||||
[`errors.Is`]: https://pkg.go.dev/errors#Is
|
||||
|
||||
<a id="test-structure"></a>
|
||||
|
||||
|
@ -3471,6 +3431,9 @@ t.Run("check that there is no mention of scratched records or hovercrafts", ...)
|
|||
t.Run("AM/PM confusion", ...)
|
||||
```
|
||||
|
||||
See also
|
||||
[Go Tip #117: Subtest Names](https://google.github.io/styleguide/go/index.html#gotip).
|
||||
|
||||
[Go test runner]: https://golang.org/cmd/go/#hdr-Testing_flags
|
||||
[identify the inputs]: #identify-the-input
|
||||
[special meaning for test filters]: https://blog.golang.org/subtests#:~:text=Perhaps%20a%20bit,match%20any%20tests
|
||||
|
@ -3491,17 +3454,16 @@ similar testing logic.
|
|||
[tests of `fmt.Sprintf`]: https://cs.opensource.google/go/go/+/master:src/fmt/fmt_test.go
|
||||
[tests for `net.Dial`]: https://cs.opensource.google/go/go/+/master:src/net/dial_test.go;l=318;drc=5b606a9d2b7649532fe25794fa6b99bd24e7697c
|
||||
|
||||
Here is the minimal structure of a table-driven test, copied from the standard
|
||||
`strings` library. If needed, you may use different names, move the test slice
|
||||
into the test function, or add extra facilities such as subtests or setup and
|
||||
cleanup functions. Always keep [useful test failures](#useful-test-failures) in
|
||||
mind.
|
||||
Here is the minimal structure of a table-driven test. If needed, you may use
|
||||
different names or add extra facilities such as subtests or setup and cleanup
|
||||
functions. Always keep [useful test failures](#useful-test-failures) in mind.
|
||||
|
||||
```go
|
||||
// Good:
|
||||
var compareTests = []struct {
|
||||
func TestCompare(t *testing.T) {
|
||||
compareTests := []struct {
|
||||
a, b string
|
||||
i int
|
||||
want int
|
||||
}{
|
||||
{"", "", 0},
|
||||
{"a", "", 1},
|
||||
|
@ -3519,11 +3481,10 @@ var compareTests = []struct {
|
|||
{"abcdefghi", "abcdefghj", -1},
|
||||
}
|
||||
|
||||
func TestCompare(t *testing.T) {
|
||||
for _, tt := range compareTests {
|
||||
cmp := Compare(tt.a, tt.b)
|
||||
if cmp != tt.i {
|
||||
t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp)
|
||||
for _, test := range compareTests {
|
||||
got := Compare(test.a, test.b)
|
||||
if got != test.want {
|
||||
t.Errorf("Compare(%q, %q) = %v, want %v", test.a, test.b, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3639,7 +3600,7 @@ func TestDecode(t *testing.T) {
|
|||
case prod:
|
||||
codex = setupCodex(t)
|
||||
default:
|
||||
t.Fatalf("unknown codex type: %v", codex)
|
||||
t.Fatalf("Unknown codex type: %v", codex)
|
||||
}
|
||||
output, err := Decode(test.input, codex)
|
||||
if got, want := output, test.output; got != want {
|
||||
|
@ -3673,7 +3634,7 @@ tests := []struct {
|
|||
}
|
||||
for i, d := range tests {
|
||||
if strings.ToUpper(d.input) != d.want {
|
||||
t.Errorf("failed on case #%d", i)
|
||||
t.Errorf("Failed on case #%d", i)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -419,8 +419,8 @@ initial capitalization.
|
|||
|
||||
### Line length
|
||||
|
||||
There is no fixed line length for Go source code. If a line feels too long, it
|
||||
should be refactored instead of broken. If it is already as short as it is
|
||||
There is no fixed line length for Go source code. If a line feels too long,
|
||||
prefer refactoring instead of splitting it. If it is already as short as it is
|
||||
practical for it to be, the line should be allowed to remain long.
|
||||
|
||||
Do not split a line:
|
||||
|
|
|
@ -2,13 +2,16 @@ table {
|
|||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
td, th {
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #ccc;
|
||||
padding: 2px 12px;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
code, samp, var {
|
||||
code,
|
||||
samp,
|
||||
var {
|
||||
color: #060;
|
||||
}
|
||||
|
||||
|
@ -17,16 +20,9 @@ pre {
|
|||
display: block;
|
||||
color: #060;
|
||||
background-color: #e8fff6;
|
||||
border-color: #f0fff0;
|
||||
border-style: solid;
|
||||
border-top-width: 1px;
|
||||
border-bottom-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border: 1px solid #f0fff0;
|
||||
border-left-width: 5px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
padding: 4px 12px;
|
||||
}
|
||||
|
||||
pre.badcode {
|
||||
|
@ -42,9 +38,7 @@ hr {
|
|||
}
|
||||
|
||||
html {
|
||||
margin-top:2em;
|
||||
margin-left:10%;
|
||||
margin-right:10%;
|
||||
margin: 2em 10% 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
@ -129,7 +123,7 @@ body {
|
|||
}
|
||||
|
||||
em {
|
||||
font-style: italic
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
h1,
|
||||
|
@ -142,8 +136,8 @@ h6 {
|
|||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom:.50em;
|
||||
text-align: center
|
||||
margin-bottom: 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h2,
|
||||
|
@ -152,14 +146,28 @@ h4,
|
|||
h5,
|
||||
h6 {
|
||||
margin-top: 1.5em;
|
||||
margin-bottom:.75em;
|
||||
margin-bottom: 0.75em;
|
||||
}
|
||||
|
||||
h1 {font-size:200%;}
|
||||
h2 {font-size:167%;}
|
||||
h3 {font-size:133%;}
|
||||
h4 {font-size:120%;}
|
||||
h5 {font-size:110%;}
|
||||
h1 {
|
||||
font-size: 200%;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 167%;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 133%;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 1.5em;
|
||||
|
@ -196,7 +204,7 @@ tt {
|
|||
}
|
||||
|
||||
dl {
|
||||
margin:0 0 1.5em 0;
|
||||
margin: 0 0 1.5em;
|
||||
}
|
||||
|
||||
dl dt {
|
||||
|
@ -406,7 +414,7 @@ ul.nolist {
|
|||
code,
|
||||
kbd,
|
||||
pre {
|
||||
color:#009900;
|
||||
color: #090;
|
||||
}
|
||||
|
||||
kbd {
|
||||
|
@ -419,7 +427,7 @@ table.striped code {
|
|||
|
||||
pre {
|
||||
padding: 6px 10px;
|
||||
background-color:#FAFAFA;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #bbb;
|
||||
overflow: auto;
|
||||
}
|
||||
|
@ -429,10 +437,13 @@ pre.prettyprint {
|
|||
border: 1px solid #bbb !important;
|
||||
}
|
||||
|
||||
code.bad, code.badcode {
|
||||
code.bad,
|
||||
code.badcode {
|
||||
color: magenta;
|
||||
}
|
||||
pre.bad, pre.badcode {
|
||||
|
||||
pre.bad,
|
||||
pre.badcode {
|
||||
background-color: #ffe6d8;
|
||||
border-top: 1px inset #a03;
|
||||
border-left: 1px inset #a03;
|
||||
|
@ -451,7 +462,6 @@ pre.bad, pre.badcode {
|
|||
}
|
||||
|
||||
@media print {
|
||||
|
||||
.str {
|
||||
color: #060;
|
||||
}
|
||||
|
@ -509,12 +519,11 @@ ol.linenums {
|
|||
}
|
||||
|
||||
code {
|
||||
background-color:#FAFAFA;
|
||||
background-color: #fafafa;
|
||||
padding: 0.25em 0.5em;
|
||||
white-space: nowrap
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
/* TOC CSS */
|
||||
|
||||
table.columns {
|
||||
|
@ -552,7 +561,7 @@ li.toc_entry {
|
|||
* at href boundaries
|
||||
*/
|
||||
li.toc_entry::after {
|
||||
content: " ";
|
||||
content: ' ';
|
||||
}
|
||||
|
||||
li.toc_entry a {
|
||||
|
@ -560,7 +569,8 @@ li.toc_entry a {
|
|||
}
|
||||
|
||||
/* Horizontal TOC */
|
||||
.toc td, .toc th {
|
||||
.toc td,
|
||||
.toc th {
|
||||
border-width: 1px 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
@ -568,7 +578,7 @@ li.toc_entry a {
|
|||
/* Vertical TOC */
|
||||
|
||||
.toc td.two_columns {
|
||||
border-width: 0px;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
/* Numbered sections */
|
||||
|
|
353
pyguide.md
353
pyguide.md
|
@ -47,6 +47,7 @@ See README.md for details.
|
|||
+ [3.8.2 Modules](#s3.8.2-comments-in-modules)
|
||||
+ [3.8.2.1 Test modules](#s3.8.2.1-test-modules)
|
||||
+ [3.8.3 Functions and Methods](#s3.8.3-functions-and-methods)
|
||||
+ [3.8.3.1 Overridden Methods](#s3.8.3.1-overridden-methods)
|
||||
+ [3.8.4 Classes](#s3.8.4-comments-in-classes)
|
||||
+ [3.8.5 Block and Inline Comments](#s3.8.5-block-and-inline-comments)
|
||||
+ [3.8.6 Punctuation, Spelling, and Grammar](#s3.8.6-punctuation-spelling-and-grammar)
|
||||
|
@ -213,8 +214,8 @@ that the arguments are actually unused.
|
|||
<a id="imports"></a>
|
||||
### 2.2 Imports
|
||||
|
||||
Use `import` statements for packages and modules only, not for individual
|
||||
classes or functions.
|
||||
Use `import` statements for packages and modules only, not for individual types,
|
||||
classes, or functions.
|
||||
|
||||
<a id="s2.2.1-definition"></a>
|
||||
<a id="221-definition"></a>
|
||||
|
@ -404,11 +405,15 @@ Exceptions must follow certain conditions:
|
|||
|
||||
- Make use of built-in exception classes when it makes sense. For example,
|
||||
raise a `ValueError` to indicate a programming mistake like a violated
|
||||
precondition (such as if you were passed a negative number but required a
|
||||
positive one). Do not use `assert` statements for validating argument values
|
||||
of a public API. `assert` is used to ensure internal correctness, not to
|
||||
enforce correct usage nor to indicate that some unexpected event occurred.
|
||||
If an exception is desired in the latter cases, use a raise statement. For
|
||||
precondition, such as may happen when validating function arguments.
|
||||
|
||||
- Do not use `assert` statements in place of conditionals or validating
|
||||
preconditions. They must not be critical to the application logic. A litmus
|
||||
test would be that the `assert` could be removed without breaking the code.
|
||||
`assert` conditionals are
|
||||
[not guaranteed](https://docs.python.org/3/reference/simple_stmts.html#the-assert-statement)
|
||||
to be evaluated. For [pytest](https://pytest.org) based tests, `assert` is
|
||||
okay and expected to verify expectations. For
|
||||
example:
|
||||
|
||||
|
||||
|
@ -435,6 +440,7 @@ Exceptions must follow certain conditions:
|
|||
if port is None:
|
||||
raise ConnectionError(
|
||||
f'Could not connect to service on port {minimum} or higher.')
|
||||
# The code does not depend on the result of this assert.
|
||||
assert port >= minimum, (
|
||||
f'Unexpected port {port} when minimum was {minimum}.')
|
||||
return port
|
||||
|
@ -452,8 +458,10 @@ Exceptions must follow certain conditions:
|
|||
The new minimum port.
|
||||
"""
|
||||
assert minimum >= 1024, 'Minimum port must be at least 1024.'
|
||||
# The following code depends on the previous assert.
|
||||
port = self._find_next_open_port(minimum)
|
||||
assert port is not None
|
||||
# The type checking of the return statement relies on the assert.
|
||||
return port
|
||||
```
|
||||
|
||||
|
@ -642,20 +650,18 @@ Complicated comprehensions or generator expressions can be hard to read.
|
|||
<a id="comprehensions-decision"></a>
|
||||
#### 2.7.4 Decision
|
||||
|
||||
Okay to use for simple cases. Each portion must fit on one line: mapping
|
||||
expression, `for` clause, filter expression. Multiple `for` clauses or filter
|
||||
expressions are not permitted. Use loops instead when things get more
|
||||
complicated.
|
||||
Comprehensions are allowed, however multiple `for` clauses or filter expressions
|
||||
are not permitted. Optimize for readability, not conciseness.
|
||||
|
||||
```python
|
||||
Yes:
|
||||
result = [mapping_expr for value in iterable if filter_expr]
|
||||
|
||||
result = [{'key': value} for value in iterable
|
||||
if a_long_filter_expression(value)]
|
||||
|
||||
result = [complicated_transform(x)
|
||||
for x in iterable if predicate(x)]
|
||||
result = [
|
||||
is_valid(metric={'key': value})
|
||||
for value in interesting_iterable
|
||||
if a_longer_filter_expression(value)
|
||||
]
|
||||
|
||||
descriptive_name = [
|
||||
transform({'key': key, 'value': value}, color='black')
|
||||
|
@ -669,32 +675,29 @@ Yes:
|
|||
if x * y > 10:
|
||||
result.append((x, y))
|
||||
|
||||
return {x: complicated_transform(x)
|
||||
return {
|
||||
x: complicated_transform(x)
|
||||
for x in long_generator_function(parameter)
|
||||
if x is not None}
|
||||
if x is not None
|
||||
}
|
||||
|
||||
squares_generator = (x**2 for x in range(10))
|
||||
return (x**2 for x in range(10))
|
||||
|
||||
unique_names = {user.name for user in users if user is not None}
|
||||
|
||||
eat(jelly_bean for jelly_bean in jelly_beans
|
||||
if jelly_bean.color == 'black')
|
||||
```
|
||||
|
||||
```python
|
||||
No:
|
||||
result = [complicated_transform(
|
||||
x, some_argument=x+1)
|
||||
for x in iterable if predicate(x)]
|
||||
|
||||
result = [(x, y) for x in range(10) for y in range(5) if x * y > 10]
|
||||
|
||||
return ((x, y, z)
|
||||
return (
|
||||
(x, y, z)
|
||||
for x in range(5)
|
||||
for y in range(5)
|
||||
if x != y
|
||||
for z in range(5)
|
||||
if y != z)
|
||||
if y != z
|
||||
)
|
||||
```
|
||||
|
||||
<a id="s2.8-default-iterators-and-operators"></a>
|
||||
|
@ -848,8 +851,8 @@ function may only contain an expression.
|
|||
<a id="lambdas-decision"></a>
|
||||
#### 2.10.4 Decision
|
||||
|
||||
Okay to use them for one-liners. If the code inside the lambda function is
|
||||
longer than 60-80 chars, it's probably better to define it as a regular
|
||||
Lambdas are allowed. If the code inside the lambda function spans multiple lines
|
||||
or is longer than 60-80 chars, it might be better to define it as a regular
|
||||
[nested function](#lexical-scoping).
|
||||
|
||||
For common operations like multiplication, use the functions from the `operator`
|
||||
|
@ -992,7 +995,7 @@ _FOO = flags.DEFINE_string(...)
|
|||
|
||||
No: def foo(a, b=[]):
|
||||
...
|
||||
No: def foo(a, b=time.time()): # The time the module was loaded???
|
||||
No: def foo(a, b=time.time()): # Is `b` supposed to represent when this module was loaded?
|
||||
...
|
||||
No: def foo(a, b=_FOO.value): # sys.argv has not yet been parsed...
|
||||
...
|
||||
|
@ -1071,7 +1074,7 @@ implement computations a subclass may ever want to override and extend.
|
|||
<a id="truefalse-evaluations"></a>
|
||||
### 2.14 True/False Evaluations
|
||||
|
||||
Use the "implicit" false if at all possible.
|
||||
Use the "implicit" false if at all possible (with a few caveats).
|
||||
|
||||
<a id="s2.14.1-definition"></a>
|
||||
<a id="2141-definition"></a>
|
||||
|
@ -1798,6 +1801,150 @@ Trailing commas in sequences of items are recommended only when the closing
|
|||
container token `]`, `)`, or `}` does not appear on the same line as the final
|
||||
element, as well as for tuples with a single element. The presence of a trailing
|
||||
comma is also used as a hint to our Python code auto-formatter
|
||||
[Black](https://github.com/psf/black) or [Pyink](https://github.com/google/pyink)
|
||||
to direct it to auto-format the container of items to one item per line when the
|
||||
`,` after the final element is present.
|
||||
|
||||
```python
|
||||
Yes: golomb3 = [0, 1, 3]
|
||||
golomb4 = [
|
||||
0,
|
||||
1,
|
||||
4,
|
||||
6,
|
||||
]
|
||||
```
|
||||
|
||||
```python
|
||||
No: golomb4 = [
|
||||
0,
|
||||
1,
|
||||
4,
|
||||
6,]
|
||||
```
|
||||
|
||||
<a id="s3.5-blank-lines"></a>
|
||||
<a id="35-blank-lines"></a>
|
||||
|
||||
<a id="blank-lines"></a>
|
||||
### 3.5 Blank Lines
|
||||
|
||||
Two blank lines between top-level definitions, be they function or class
|
||||
definitions. One blank line between method definitions and between the docstring
|
||||
of a `class` and the first method. No blank line following a `def` line. Use
|
||||
single blank lines as you judge appropriate within functions or methods.
|
||||
|
||||
Blank lines need not be anchored to the definition. For example, related
|
||||
comments immediately preceding function, class, and method definitions can make
|
||||
sense. Consider if your comment might be more useful as part of the docstring.
|
||||
|
||||
<a id="s3.6-whitespace"></a>
|
||||
<a id="36-whitespace"></a>
|
||||
|
||||
<a id="whitespace"></a>
|
||||
### 3.6 Whitespace
|
||||
|
||||
Follow standard typographic rules for the use of spaces around punctuation.
|
||||
|
||||
No whitespace inside parentheses, brackets or braces.
|
||||
|
||||
```python
|
||||
Yes: spam(ham[1], {'eggs': 2}, [])
|
||||
```
|
||||
|
||||
```python
|
||||
No: spam( ham[ 1 ], { 'eggs': 2 }, [ ] )
|
||||
```
|
||||
|
||||
No whitespace before a comma, semicolon, or colon. Do use whitespace after a
|
||||
comma, semicolon, or colon, except at the end of the line.
|
||||
|
||||
```python
|
||||
Yes: if x == 4:
|
||||
print(x, y)
|
||||
x, y = y, x
|
||||
```
|
||||
|
||||
```python
|
||||
No: if x == 4 :
|
||||
print(x , y)
|
||||
x , y = y , x
|
||||
```
|
||||
|
||||
No whitespace before the open paren/bracket that starts an argument list,
|
||||
indexing or slicing.
|
||||
|
||||
```python
|
||||
Yes: spam(1)
|
||||
```
|
||||
|
||||
```python
|
||||
No: spam (1)
|
||||
```
|
||||
|
||||
```python
|
||||
Yes: dict['key'] = list[index]
|
||||
```
|
||||
|
||||
```python
|
||||
No: dict ['key'] = list [index]
|
||||
```
|
||||
|
||||
No trailing whitespace.
|
||||
|
||||
Surround binary operators with a single space on either side for assignment
|
||||
(`=`), comparisons (`==, <, >, !=, <>, <=, >=, in, not in, is, is not`), and
|
||||
Booleans (`and, or, not`). Use your better judgment for the insertion of spaces
|
||||
around arithmetic operators (`+`, `-`, `*`, `/`, `//`, `%`, `**`, `@`).
|
||||
|
||||
```python
|
||||
Yes: x == 1
|
||||
```
|
||||
|
||||
```python
|
||||
No: x<1
|
||||
```
|
||||
|
||||
Never use spaces around `=` when passing keyword arguments or defining a default
|
||||
parameter value, with one exception:
|
||||
[when a type annotation is present](#typing-default-values), *do* use spaces
|
||||
around the `=` for the default parameter value.
|
||||
|
||||
```python
|
||||
Yes: def complex(real, imag=0.0): return Magic(r=real, i=imag)
|
||||
Yes: def complex(real, imag: float = 0.0): return Magic(r=real, i=imag)
|
||||
```
|
||||
|
||||
```python
|
||||
No: def complex(real, imag = 0.0): return Magic(r = real, i = imag)
|
||||
No: def complex(real, imag: float=0.0): return Magic(r = real, i = imag)
|
||||
```
|
||||
|
||||
Don't use spaces to vertically align tokens on consecutive lines, since it
|
||||
becomes a maintenance burden (applies to `:`, `#`, `=`, etc.):
|
||||
|
||||
```python
|
||||
Yes:
|
||||
foo = 1000 # comment
|
||||
long_name = 2 # comment that should not be aligned
|
||||
|
||||
dictionary = {
|
||||
'foo': 1,
|
||||
'long_name': 2,
|
||||
}
|
||||
```
|
||||
|
||||
```python
|
||||
No:
|
||||
foo = 1000 # comment
|
||||
long_name = 2 # comment that should not be aligned
|
||||
|
||||
dictionary = {
|
||||
'foo' : 1,
|
||||
'long_name': 2,
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
<a id="Python_Interpreter"></a>
|
||||
<a id="s3.7-shebang-line"></a>
|
||||
|
@ -1929,15 +2076,6 @@ should use the same style as the docstring for an attribute or a
|
|||
<a href="#doc-function-args">function argument</a> (`"""The Bigtable path."""`,
|
||||
rather than `"""Returns the Bigtable path."""`).
|
||||
|
||||
A method that overrides a method from a base class may have a simple docstring
|
||||
sending the reader to its overridden method's docstring, such as `"""See base
|
||||
class."""`. The rationale is that there is no need to repeat in many places
|
||||
documentation that is already present in the base method's docstring. However,
|
||||
if the overriding method's behavior is substantially different from the
|
||||
overridden method, or details need to be provided (e.g., documenting additional
|
||||
side effects), a docstring with at least those differences is required on the
|
||||
overriding method.
|
||||
|
||||
Certain aspects of a function should be documented in special sections, listed
|
||||
below. Each section begins with a heading line, which ends with a colon. All
|
||||
sections other than the heading should maintain a hanging indent of two or four
|
||||
|
@ -1961,16 +2099,18 @@ aptly described using a one-line docstring.
|
|||
: Describe the semantics of the return value, including any type information
|
||||
that the type annotation does not provide. If the function only returns
|
||||
None, this section is not required. It may also be omitted if the docstring
|
||||
starts with Returns or Yields (e.g. `"""Returns row from Bigtable as a tuple
|
||||
of strings."""`) and the opening sentence is sufficient to describe the
|
||||
return value. Do not imitate 'NumPy style'
|
||||
([example](http://numpy.org/doc/stable/reference/generated/numpy.linalg.qr.html)),
|
||||
which frequently documents a tuple return value as if it were multiple
|
||||
starts with "Return", "Returns", "Yield", or "Yields" (e.g. `"""Returns row
|
||||
from Bigtable as a tuple of strings."""`) *and* the opening sentence is
|
||||
sufficient to describe the return value. Do not imitate older 'NumPy style'
|
||||
([example](https://numpy.org/doc/1.24/reference/generated/numpy.linalg.qr.html)),
|
||||
which frequently documented a tuple return value as if it were multiple
|
||||
return values with individual names (never mentioning the tuple). Instead,
|
||||
describe such a return value as: "Returns: A tuple (mat_a, mat_b), where
|
||||
mat_a is ..., and ...". The auxiliary names in the docstring need not
|
||||
necessarily correspond to any internal names used in the function body (as
|
||||
those are not part of the API).
|
||||
those are not part of the API). If the function uses `yield` (is a
|
||||
generator), the `Yields:` section should document the object returned by
|
||||
`next()`, instead of the generator object itself that the call evaluates to.
|
||||
|
||||
<a id="doc-function-raises"></a>
|
||||
[*Raises:*](#doc-function-raises)
|
||||
|
@ -2057,6 +2197,47 @@ def fetch_smalltable_rows(
|
|||
"""
|
||||
```
|
||||
|
||||
<a id="s3.8.3.1-overridden-methods"></a>
|
||||
|
||||
<a id="overridden-method-docs"></a>
|
||||
##### 3.8.3.1 Overridden Methods
|
||||
|
||||
A method that overrides a method from a base class does not need a docstring if
|
||||
it is explicitly decorated with
|
||||
[`@override`](https://typing-extensions.readthedocs.io/en/latest/#override)
|
||||
(from `typing_extensions` or `typing` modules), unless the overriding method's
|
||||
behavior materially refines the base method's contract, or details need to be
|
||||
provided (e.g., documenting additional side effects), in which case a docstring
|
||||
with at least those differences is required on the overriding method.
|
||||
|
||||
```python
|
||||
from typing_extensions import override
|
||||
|
||||
class Parent:
|
||||
def do_something(self):
|
||||
"""Parent method, includes docstring."""
|
||||
|
||||
# Child class, method annotated with override.
|
||||
class Child(Parent):
|
||||
@override
|
||||
def do_something(self):
|
||||
pass
|
||||
```
|
||||
|
||||
```python
|
||||
# Child class, but without @override decorator, a docstring is required.
|
||||
class Child(Parent):
|
||||
def do_something(self):
|
||||
pass
|
||||
|
||||
# Docstring is trivial, @override is sufficient to indicate that docs can be
|
||||
# found in the base class.
|
||||
class Child(Parent):
|
||||
@override
|
||||
def do_something(self):
|
||||
"""See base class."""
|
||||
```
|
||||
|
||||
<a id="s3.8.4-comments-in-classes"></a>
|
||||
<a id="384-classes"></a>
|
||||
<a id="comments-in-classes"></a>
|
||||
|
@ -2065,8 +2246,8 @@ def fetch_smalltable_rows(
|
|||
#### 3.8.4 Classes
|
||||
|
||||
Classes should have a docstring below the class definition describing the class.
|
||||
If your class has public attributes, they should be documented here in an
|
||||
`Attributes` section and follow the same formatting as a
|
||||
Public attributes, excluding [properties](#properties), should be documented
|
||||
here in an `Attributes` section and follow the same formatting as a
|
||||
[function's `Args`](#doc-function-args) section.
|
||||
|
||||
```python
|
||||
|
@ -2090,8 +2271,9 @@ class SampleClass:
|
|||
self.likes_spam = likes_spam
|
||||
self.eggs = 0
|
||||
|
||||
def public_method(self):
|
||||
"""Performs operation blah."""
|
||||
@property
|
||||
def butter_sticks(self) -> int:
|
||||
"""The number of butter sticks we have."""
|
||||
```
|
||||
|
||||
All class docstrings should start with a one-line summary that describes what
|
||||
|
@ -2366,7 +2548,7 @@ messages shown to the user) should follow three guidelines:
|
|||
```python
|
||||
Yes:
|
||||
if not 0 <= p <= 1:
|
||||
raise ValueError(f'Not a probability: {p!r}')
|
||||
raise ValueError(f'Not a probability: {p=}')
|
||||
|
||||
try:
|
||||
os.rmdir(workdir)
|
||||
|
@ -2378,7 +2560,7 @@ messages shown to the user) should follow three guidelines:
|
|||
```python
|
||||
No:
|
||||
if p < 0 or p > 1: # PROBLEM: also false for float('nan')!
|
||||
raise ValueError(f'Not a probability: {p!r}')
|
||||
raise ValueError(f'Not a probability: {p=}')
|
||||
|
||||
try:
|
||||
os.rmdir(workdir)
|
||||
|
@ -2477,27 +2659,36 @@ documentation must explain clearly how resource lifetime is managed.
|
|||
Use `TODO` comments for code that is temporary, a short-term solution, or
|
||||
good-enough but not perfect.
|
||||
|
||||
A `TODO` comment begins with the word `TODO` in all caps, and a parenthesized
|
||||
context identifier. Ideally a bug reference, sometimes a username. A bug
|
||||
reference like `TODO(https://crbug.com/bug_id_number):` is
|
||||
preferable, because bugs are tracked and have follow-up comments, whereas
|
||||
individuals move around and may lose context over time. The `TODO` is followed by an explanation of
|
||||
what there is to do.
|
||||
|
||||
A `TODO` comment begins with the word `TODO` in all caps, a following colon, and
|
||||
a link to a resource that contains the context, ideally a bug reference. A bug
|
||||
reference is preferable because bugs are tracked and have follow-up comments.
|
||||
Follow this piece of context with an explanatory string introduced with a hyphen
|
||||
`-`.
|
||||
The purpose is to have a consistent `TODO` format that can be searched to find
|
||||
out how to get more details. A `TODO` is not a commitment that the person
|
||||
referenced will fix the problem. Thus when you create a `TODO` with a username,
|
||||
it is almost always your *own* username that is given.
|
||||
out how to get more details.
|
||||
|
||||
```python
|
||||
# TODO: crbug.com/192795 - Investigate cpufreq optimizations.
|
||||
```
|
||||
|
||||
Old style, formerly recommended, but discouraged for use in new code:
|
||||
|
||||
|
||||
```python
|
||||
# TODO(crbug.com/192795): Investigate cpufreq optimizations.
|
||||
# TODO(yourusername): File an issue and use a '*' for repetition.
|
||||
# TODO(yourusername): Use a "\*" here for concatenation operator.
|
||||
```
|
||||
|
||||
Avoid adding TODOs that refer to an individual or team as the context:
|
||||
|
||||
```python
|
||||
# TODO: @yourusername - File an issue and use a '*' for repetition.
|
||||
```
|
||||
|
||||
If your `TODO` is of the form "At a future date do something" make sure that you
|
||||
either include a very specific date ("Fix by November 2009") or a very specific
|
||||
event ("Remove this code when all clients can handle XML responses.") that
|
||||
future code maintainers will comprehend.
|
||||
future code maintainers will comprehend. Issues are ideal for tracking this.
|
||||
|
||||
<a id="s3.13-imports-formatting"></a>
|
||||
<a id="313-imports-formatting"></a>
|
||||
|
@ -2722,7 +2913,9 @@ Always use a `.py` filename extension. Never use dashes.
|
|||
class.
|
||||
|
||||
- Prepending a single underscore (`_`) has some support for protecting module
|
||||
variables and functions (linters will flag protected member access).
|
||||
variables and functions (linters will flag protected member access). Note
|
||||
that it is okay for unit tests to access protected constants from the
|
||||
modules under test.
|
||||
|
||||
- Prepending a double underscore (`__` aka "dunder") to an instance variable
|
||||
or method effectively makes the variable or method private to its class
|
||||
|
@ -2930,13 +3123,20 @@ the function into smaller and more manageable pieces.
|
|||
|
||||
* Familiarize yourself with [PEP-484](https://peps.python.org/pep-0484/).
|
||||
|
||||
* In methods, only annotate `self`, or `cls` if it is necessary for proper
|
||||
type information. e.g.,
|
||||
* Annotating `self` or `cls` is generally not necessary.
|
||||
[`Self`](https://docs.python.org/3/library/typing.html#typing.Self) can be
|
||||
used if it is necessary for proper type information, e.g.
|
||||
|
||||
```python
|
||||
from typing import Self
|
||||
|
||||
class BaseClass:
|
||||
@classmethod
|
||||
def create(cls: Type[_T]) -> _T:
|
||||
return cls()
|
||||
def create(cls) -> Self:
|
||||
...
|
||||
|
||||
def difference(self, other: Self) -> float:
|
||||
...
|
||||
```
|
||||
|
||||
* Similarly, don't feel compelled to annotate the return value of `__init__`
|
||||
|
@ -3327,15 +3527,16 @@ return type is the same as the argument type in the code above, use
|
|||
<a id="typing-imports"></a>
|
||||
#### 3.19.12 Imports For Typing
|
||||
|
||||
For symbols from the `typing` and `collections.abc` modules used to support
|
||||
static analysis and type checking, always import the symbol itself. This keeps
|
||||
common annotations more concise and matches typing practices used around the
|
||||
world. You are explicitly allowed to import multiple specific classes on one
|
||||
line from the `typing` and `collections.abc` modules. Ex:
|
||||
For symbols (including types, functions, and constants) from the `typing` or
|
||||
`collections.abc` modules used to support static analysis and type checking,
|
||||
always import the symbol itself. This keeps common annotations more concise and
|
||||
matches typing practices used around the world. You are explicitly allowed to
|
||||
import multiple specific symbols on one line from the `typing` and
|
||||
`collections.abc` modules. For example:
|
||||
|
||||
```python
|
||||
from collections.abc import Mapping, Sequence
|
||||
from typing import Any, Generic
|
||||
from typing import Any, Generic, cast, TYPE_CHECKING
|
||||
```
|
||||
|
||||
Given that this way of importing adds items to the local namespace, names in
|
||||
|
|
40
pylintrc
40
pylintrc
|
@ -5,7 +5,7 @@
|
|||
# Its canonical open-source location is:
|
||||
# https://google.github.io/styleguide/pylintrc
|
||||
|
||||
[MASTER]
|
||||
[MAIN]
|
||||
|
||||
# Files or directories to be skipped. They should be base names, not paths.
|
||||
ignore=third_party
|
||||
|
@ -50,7 +50,8 @@ confidence=
|
|||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use"--disable=all --enable=classes
|
||||
# --disable=W"
|
||||
disable=abstract-method,
|
||||
disable=R,
|
||||
abstract-method,
|
||||
apply-builtin,
|
||||
arguments-differ,
|
||||
attribute-defined-outside-init,
|
||||
|
@ -66,7 +67,6 @@ disable=abstract-method,
|
|||
coerce-method,
|
||||
delslice-method,
|
||||
div-method,
|
||||
duplicate-code,
|
||||
eq-without-hash,
|
||||
execfile-builtin,
|
||||
file-builtin,
|
||||
|
@ -80,7 +80,6 @@ disable=abstract-method,
|
|||
import-error,
|
||||
import-self,
|
||||
import-star-module-level,
|
||||
inconsistent-return-statements,
|
||||
input-builtin,
|
||||
intern-builtin,
|
||||
invalid-str-codec,
|
||||
|
@ -94,10 +93,6 @@ disable=abstract-method,
|
|||
next-method-called,
|
||||
next-method-defined,
|
||||
no-absolute-import,
|
||||
no-else-break,
|
||||
no-else-continue,
|
||||
no-else-raise,
|
||||
no-else-return,
|
||||
no-init, # added
|
||||
no-member,
|
||||
no-name-in-module,
|
||||
|
@ -123,24 +118,12 @@ disable=abstract-method,
|
|||
standarderror-builtin,
|
||||
suppressed-message,
|
||||
sys-max-int,
|
||||
too-few-public-methods,
|
||||
too-many-ancestors,
|
||||
too-many-arguments,
|
||||
too-many-boolean-expressions,
|
||||
too-many-branches,
|
||||
too-many-instance-attributes,
|
||||
too-many-locals,
|
||||
too-many-nested-blocks,
|
||||
too-many-public-methods,
|
||||
too-many-return-statements,
|
||||
too-many-statements,
|
||||
trailing-newlines,
|
||||
unichr-builtin,
|
||||
unicode-builtin,
|
||||
unnecessary-pass,
|
||||
unpacking-in-except,
|
||||
useless-else-on-loop,
|
||||
useless-object-inheritance,
|
||||
useless-suppression,
|
||||
using-cmp-argument,
|
||||
wrong-import-order,
|
||||
|
@ -225,7 +208,7 @@ no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$
|
|||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
docstring-min-length=10
|
||||
docstring-min-length=12
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
@ -235,10 +218,6 @@ docstring-min-length=10
|
|||
# produce valid context managers.
|
||||
contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager
|
||||
|
||||
# Tells whether missing members accessed in mixin class should be ignored. A
|
||||
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||
ignore-mixin-members=yes
|
||||
|
||||
# List of module names for which member attributes should not be checked
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis. It
|
||||
|
@ -261,7 +240,7 @@ generated-members=
|
|||
# Maximum number of characters on a single line.
|
||||
max-line-length=80
|
||||
|
||||
# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt
|
||||
# TODO(https://github.com/pylint-dev/pylint/issues/3352): Direct pylint to exempt
|
||||
# lines made too long by directives to pytype.
|
||||
|
||||
# Regexp for a line that is allowed to be longer than the limit.
|
||||
|
@ -418,12 +397,3 @@ valid-classmethod-first-arg=cls,
|
|||
|
||||
# List of valid names for the first argument in a metaclass class method.
|
||||
valid-metaclass-classmethod-first-arg=mcs
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when being caught. Defaults to
|
||||
# "Exception"
|
||||
overgeneral-exceptions=builtins.StandardError,
|
||||
builtins.Exception,
|
||||
builtins.BaseException
|
||||
|
|
|
@ -7,7 +7,13 @@ body {
|
|||
margin-left: 100px;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6, .toc_title {
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
.toc_title {
|
||||
color: #06c;
|
||||
margin-top: 2em;
|
||||
margin-bottom: 1em;
|
||||
|
@ -18,38 +24,47 @@ h1 {
|
|||
font-size: 18pt;
|
||||
}
|
||||
|
||||
h2, .toc_title {
|
||||
h2,
|
||||
.toc_title {
|
||||
font-weight: bold;
|
||||
font-size: 12pt;
|
||||
margin-left: -40px;
|
||||
}
|
||||
|
||||
h3, h4, h5, h6 {
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: 10pt;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
.toc_category, .toc_stylepoint {
|
||||
.toc_category,
|
||||
.toc_stylepoint {
|
||||
font-size: 10pt;
|
||||
padding-top: .3em;
|
||||
padding-bottom: .3em;
|
||||
padding-top: 0.3em;
|
||||
padding-bottom: 0.3em;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
td, th {
|
||||
td,
|
||||
th {
|
||||
border: 1px solid #ccc;
|
||||
padding: 2px 12px;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.toc td, .toc th {
|
||||
.toc td,
|
||||
.toc th {
|
||||
border-width: 1px 5px;
|
||||
}
|
||||
|
||||
code, samp, var {
|
||||
code,
|
||||
samp,
|
||||
var {
|
||||
color: #060;
|
||||
}
|
||||
|
||||
|
@ -58,16 +73,9 @@ pre {
|
|||
display: block;
|
||||
color: #060;
|
||||
background-color: #f8fff8;
|
||||
border-color: #f0fff0;
|
||||
border-style: solid;
|
||||
border-top-width: 1px;
|
||||
border-bottom-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border: 1px solid #f0fff0;
|
||||
border-left-width: 5px;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
padding: 4px 12px;
|
||||
}
|
||||
|
||||
pre.badcode {
|
||||
|
@ -79,8 +87,7 @@ pre.badcode {
|
|||
.showhide_button {
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border: 1px solid;
|
||||
border-color: #ddd #aaa #aaa #ddd;
|
||||
padding: 0 3px 1px;
|
||||
margin: 0 4px 8px 0;
|
||||
|
@ -93,9 +100,7 @@ pre.badcode {
|
|||
float: left;
|
||||
display: none;
|
||||
background-color: #f8f8ff;
|
||||
border-color: #f0f0ff;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border: 1px solid #f0f0ff;
|
||||
font-size: 75%;
|
||||
margin-top: 0;
|
||||
margin-left: -50px;
|
||||
|
@ -118,7 +123,7 @@ hr {
|
|||
.stylepoint_section {
|
||||
display: block;
|
||||
margin-bottom: 1em;
|
||||
color: #5588ff;
|
||||
color: #58f;
|
||||
font-family: sans-serif;
|
||||
font-size: 90%;
|
||||
font-weight: bold;
|
||||
|
@ -126,7 +131,7 @@ hr {
|
|||
}
|
||||
|
||||
.stylepoint_subsection {
|
||||
color: #667799;
|
||||
color: #679;
|
||||
font-family: sans-serif;
|
||||
font-size: 90%;
|
||||
font-weight: bold;
|
||||
|
@ -134,7 +139,7 @@ hr {
|
|||
}
|
||||
|
||||
.stylepoint_subsubsection {
|
||||
color: #667799;
|
||||
color: #679;
|
||||
font-family: sans-serif;
|
||||
font-size: 80%;
|
||||
font-weight: bold;
|
||||
|
@ -144,4 +149,3 @@ hr {
|
|||
.revision {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
|
4375
tsguide.html
4375
tsguide.html
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user