diff options
| author | Ian Cordasco <graffatcolmingov@gmail.com> | 2016-12-23 09:13:26 -0600 |
|---|---|---|
| committer | Ian Cordasco <graffatcolmingov@gmail.com> | 2016-12-23 09:34:19 -0600 |
| commit | 57d8bca4af4631c0d519d03d1e77bc476c794aa0 (patch) | |
| tree | df0709af94a99819351876962d2f2c05588394c3 /docs/source/internal | |
| parent | 4bd79981625afb6e995fb83940e0f21741a3d8d4 (diff) | |
| download | flake8-57d8bca4af4631c0d519d03d1e77bc476c794aa0.tar.gz | |
Add documentation for our code style
Diffstat (limited to 'docs/source/internal')
| -rw-r--r-- | docs/source/internal/contributing.rst | 2 | ||||
| -rw-r--r-- | docs/source/internal/index.rst | 1 | ||||
| -rw-r--r-- | docs/source/internal/writing-code.rst | 220 |
3 files changed, 223 insertions, 0 deletions
diff --git a/docs/source/internal/contributing.rst b/docs/source/internal/contributing.rst index 590d8e3..80ff9ac 100644 --- a/docs/source/internal/contributing.rst +++ b/docs/source/internal/contributing.rst @@ -163,6 +163,8 @@ Merge requests should: The final line of the body references the issue appropriately. +- Follow the guidelines in :ref:`writing-code` + Reviewing and Triaging Issues and Merge Requests ================================================ diff --git a/docs/source/internal/index.rst b/docs/source/internal/index.rst index 06fcd13..9e31d0b 100644 --- a/docs/source/internal/index.rst +++ b/docs/source/internal/index.rst @@ -17,6 +17,7 @@ pull gently. contributing writing-documentation + writing-code releases start-to-finish checker diff --git a/docs/source/internal/writing-code.rst b/docs/source/internal/writing-code.rst new file mode 100644 index 0000000..de129af --- /dev/null +++ b/docs/source/internal/writing-code.rst @@ -0,0 +1,220 @@ +.. _writing-code: + +========================= + Writing Code for Flake8 +========================= + +The maintainers of |Flake8| unsurprisingly have some opinions about the styl +of code maintained in the project. + +At the time of this writing, |Flake8| enables all of PyCodeStyle's checks, all +of PyFlakes' checks, and sets a maximum complexity value (for McCabe) of 10. +On top of that, we enforce PEP-0257 style doc-strings via PyDocStyle +(disabling only D203) and Google's import order style using +flake8-import-order. + +The last two are a little unusual, so we provide examples below. + + +PEP-0257 style doc-strings +========================== + +|Flake8| attempts to document both internal interfaces as well as our API and +doc-strings provide a very convenient way to do so. Even if a function, class, +or method isn't included specifically in our documentation having a doc-string +is still preferred. Further, |Flake8| has some style preferences that are not +checked by PyDocStyle. + +For example, while most people will never read the doc-string for +:func:`flake8.main.git.hook` that doc-string still provides value to the +maintainers and future collaborators. They (very explicitly) describe the +purpose of the function, a little of what it does, and what parameters it +accepts as well as what it returns. + +.. code-block:: python + + # src/flake8/main/git.py + def hook(lazy=False, strict=False): + """Execute Flake8 on the files in git's index. + + Determine which files are about to be committed and run Flake8 over them + to check for violations. + + :param bool lazy: + Find files not added to the index prior to committing. This is useful + if you frequently use ``git commit -a`` for example. This defaults to + False since it will otherwise include files not in the index. + :param bool strict: + If True, return the total number of errors/violations found by Flake8. + This will cause the hook to fail. + :returns: + Total number of errors found during the run. + :rtype: + int + """ + # NOTE(sigmavirus24): Delay import of application until we need it. + from flake8.main import application + app = application.Application() + with make_temporary_directory() as tempdir: + filepaths = list(copy_indexed_files_to(tempdir, lazy)) + app.initialize(['.']) + app.options.exclude = update_excludes(app.options.exclude, tempdir) + app.options._running_from_vcs = True + app.run_checks(filepaths) + + app.report_errors() + if strict: + return app.result_count + return 0 + +Note that because the parameters ``hook`` and ``strict`` are simply boolean +parameters, we inline the type declaration for those parameters, e.g., + +.. code-block:: restructuredtext + + :param bool lazy: + +Also note that we begin the description of the parameter on a new-line and +indented 4 spaces. + +On the other hand, we also separate the parameter type declaration in some +places where the name is a little longer, e.g., + +.. code-block:: python + + # src/flake8/formatting/base.py + def format(self, error): + """Format an error reported by Flake8. + + This method **must** be implemented by subclasses. + + :param error: + This will be an instance of :class:`~flake8.style_guide.Error`. + :type error: + flake8.style_guide.Error + :returns: + The formatted error string. + :rtype: + str + """ + +Here we've separated ``:param error:`` and ``:type error:``. + +Following the above examples and guidelines should help you write doc-strings +that are stylistically correct for |Flake8|. + + +Imports +======= + +|Flake8| follows the import guidelines that Google published in their Python +Style Guide. In short this includes: + +- Only importing modules + +- Grouping imports into + + * standard library imports + + * third-party dependency imports + + * local application imports + +- Ordering imports alphabetically + +In practice this would look something like: + +.. code-block:: python + + import configparser + import logging + from os import path + + import requests + + from flake8 import exceptions + from flake8.formatting import base + +As a result, of the above, we do not: + +- Import objects into a namespace to make them accessible from that namespace + +- Import only the objects we're using + +- Add commnts explaining that an import is a standard library module or + something else + + +Other Stylistic Preferences +=========================== + +Finally, |Flake8| has a few other stylistic preferences that it does not +presently enforce automatically. + +Multi-line Function/Method Calls +-------------------------------- + +When you find yourself having to split a call to a function or method up +across multiple lines, insert a new-line after the opening parenthesis, e.g., + +.. code-block:: python + + # src/flake8/main/options.py + add_option( + '-v', '--verbose', default=0, action='count', + parse_from_config=True, + help='Print more information about what is happening in flake8.' + ' This option is repeatable and will increase verbosity each ' + 'time it is repeated.', + ) + + # src/flake8/formatting/base.py + def show_statistics(self, statistics): + """Format and print the statistics.""" + for error_code in statistics.error_codes(): + stats_for_error_code = statistics.statistics_for(error_code) + statistic = next(stats_for_error_code) + count = statistic.count + count += sum(stat.count for stat in stats_for_error_code) + self._write('{count:<5} {error_code} {message}'.format( + count=count, + error_code=error_code, + message=statistic.message, + )) + +In the first example, we put a few of the parameters all on one line, and then +added the last two on their own. In the second example, each parameter has its +own line. This particular rule is a little subjective. The general idea is +that putting one parameter per-line is preferred, but sometimes it's +reasonable and understandable to group a few together on one line. + +Comments +-------- + +If you're adding an important comment, be sure to sign it. In |Flake8| we +generally sign comments by preceding them with ``NOTE(<name>)``. For example, + +.. code-block:: python + + # NOTE(sigmavirus24): The format strings are a little confusing, even + # to me, so here's a quick explanation: + # We specify the named value first followed by a ':' to indicate we're + # formatting the value. + # Next we use '<' to indicate we want the value left aligned. + # Then '10' is the width of the area. + # For floats, finally, we only want only want at most 3 digits after + # the decimal point to be displayed. This is the precision and it + # can not be specified for integers which is why we need two separate + # format strings. + float_format = '{value:<10.3} {statistic}'.format + int_format = '{value:<10} {statistic}'.format + +Ian is well known across most websites as ``sigmavirus24`` so he signs his +comments that way. + +Verbs Belong in Function Names +------------------------------ + +|Flake8| prefers that functions have verbs in them. If you're writing a +function that returns a generator of files then ``generate_files`` will always +be preferable to ``make_files`` or ``files``. |
