diff --git a/docs/source/glossary.rst b/docs/source/glossary.rst index dd691b6..6717472 100644 --- a/docs/source/glossary.rst +++ b/docs/source/glossary.rst @@ -24,6 +24,7 @@ error error code + violation The symbol associated with a specific :term:`check`. For example, pycodestyle implements :term:`check`\ s that look for whitespace around binary operators and will either return an error code of diff --git a/docs/source/internal/index.rst b/docs/source/internal/index.rst index 482b898..06fcd13 100644 --- a/docs/source/internal/index.rst +++ b/docs/source/internal/index.rst @@ -18,6 +18,7 @@ pull gently. contributing writing-documentation releases + start-to-finish checker cli formatters diff --git a/docs/source/internal/start-to-finish.rst b/docs/source/internal/start-to-finish.rst new file mode 100644 index 0000000..381ef91 --- /dev/null +++ b/docs/source/internal/start-to-finish.rst @@ -0,0 +1,129 @@ +================================== + What Happens When You Run Flake8 +================================== + +Given |Flake8| 3.0's new organization and structure, it might be a bit much +for some people to understand what happens from when you call ``flake8`` on the +command-line to when it completes. This section aims to give you something of +a technical overview of what exactly happens. + + +Invocation +========== + +The exact way that we end up in our ``main`` function for Flake8 depends on +how you invoke it. If you do something like: + +.. prompt:: bash + + flake8 + +Then your shell looks up where ``flake8`` the executable lives and executes +it. In almost every case, this is a tiny python script generated by +``setuptools`` using the console script entry points that |Flake8| declares +in its :file:`setup.py`. This might look something like: + +.. code-block:: python + + #!/path/to/python + # EASY-INSTALL-ENTRY-SCRIPT: 'flake8==3.0.0','console_scripts','flake8' + __requires__ = 'flake8==3.0.0' + import sys + from pkg_resources import load_entry_point + + if __name__ == '__main__': + sys.exit( + load_entry_point('flake8==3.0.0', 'console_scripts', 'flake8')() + ) + +If instead you invoke it like: + +.. prompt:: bash + + python -m flake8 + +Then you're relying on Python to find :mod:`flake8.__main__` and run that. In +both cases, however, you end up in :func:`flake8.main.cli.main`. This is the +primary way that users will end up starting Flake8. This function creates an +instance of |Application|. + +via Setuptools +-------------- + +If you're invoking |Flake8| from your ``setup.py`` then you actually end up in +:meth:`flake8.main.setuptools_command.Flake8.run`. This then collects the +files that are included in the package information and creates an instance of +|Application|. + +via Git or Mercurial +-------------------- + +In both cases, they call their respective ``hook`` functions which create +instances of |Application|. + + +Application Logic +================= + +When we create our |Application| instance, we record the start time and parse +our command-line arguments so we can configure the verbosity of |Flake8|'s +logging. For the most part, every path then calls +:meth:`~flake8.main.application.Application.run` which in turn calls: + +- :meth:`~flake8.main.application.Application.initialize` +- :meth:`~flake8.main.application.Application.run_checks` +- :meth:`~flake8.main.application.Application.report_errors` +- :meth:`~flake8.main.application.Application.report_benchmarks` + +Our Git hook, however, runs these individually. + +Application Initialization +-------------------------- + +:meth:`~flake8.main.application.Application.initialize` loads all of our +:term:`plugin`\ s, registers the options for those plugins, parses the +command-line arguments, makes our formatter (as selected by the user), makes +our :class:`~flake8.style_guide.StyleGuide` and finally makes our +:class:`file checker manager `. + +Running Our Checks +------------------ + +:meth:`~flake8.main.application.Application.run_checks` then creates an +instance of :class:`flake8.checker.FileChecker` for each file to be checked +after aggregating all of the files that are not excluded and match the +provided file-patterns. Then, if we're on a system that supports +:mod:`multiprocessing` **and** :option:`flake8 --jobs` is either ``auto`` or +a number greater than 1, we will begin processing the files in subprocesses. +Otherwise, we'll run the checks in parallel. + +After we start running the checks, we start aggregating the reported +:term:`violation`\ s in the main process. After the checks are done running, +we record the end time. + +Reporting Violations +-------------------- + +Next, the application takes the violations from the file checker manager, and +feeds them through the :class:`~flake8.style_guide.StyleGuide`. This +determines whether the particular :term:`error code` is selected or ignored +and then appropriately sends it to the formatter (or not). + +Reporting Benchmarks +-------------------- + +Finally, if the user has asked to see benchmarks (i.e., :option:`flake8 +--benchmark`) then we print the benchmarks. + + +Exiting +======= + +Once :meth:`~flake8.main.application.Application.run` has finished, we then +call :meth:`~flake8.main.application.Application.exit` which looks at how +many errors were reported and whether the user specified :option:`flake8 +--exit-zero` and exits with the appropriate exit code. + + +.. Replacements +.. |Application| replace:: :class:`~flake8.main.application.Application`