Now that the contract has narrowed for `utils.normalize_paths()`
and `utils.parse_comma_separated_list()`, `Option.normalize()` must
handle when the option value is either a singular value or a sequence
(i.e., `list`) of values.
The paths where `Option.normalize()` is called are:
1. options/config.py: `MergedConfigParser.parse_*_config()`
There are 3 paths wehre eventually `_normalize_value()` is called.
2. options/manager.py: `OptionManager.parse_args()`
For (1), values are coming from the `configparser` module. For (2),
values are coming from `optparse.OptionParser`.
Rudimentary investigation seems to implicate that
`optparse.OptionParser.parse_args()` always returns values in a `list`
because it accumulates values. However, for `configparser`, the values
are a string due to the key-value nature of the INI format.
Given that `Option.normalize()` is the convergence point where
normalization of an option occurs, it is acceptable for the method to
also handle the parsing comma-separated values and path normalization by
the option value's type.
The `normalize_paths()` utility was doing too much — parsing
unstructured configuration path data and dispatching the scrubbed paths
to be normalized.
Towards moving the parsing of unstructured configuration path data
closer towards were configuration occurs, have the utility accept only
structured input for normalizing paths.
Move the path normalization for extra configuration file paths down into
the main `config` module where other path normalization occurs.
This also guarantees that the call to `utils.normalize_paths()` is given
a sequence, instead of a potential `None` value.
It's unnecessary to call the `normalize_paths()` function because it is
intended for dealing with multiple paths to normalize. Given that
`normalize_paths()` utilizes `normalize_path()`, just call
`normalize_path()` directly.
`Application.parse_preliminary_options_and_args` was previously, against
expectations, treating empty lists passed as the `argv` argument the
same way it treated `None`s.
This has been addressed and the correct behavior tested for in a unit
test of the `Application` class.
See issue #518 for details.
~40% improvement over status quo (perf measurements are best-of-5)
### before
```console
$ time flake8 /dev/null
real 0m0.337s
user 0m0.212s
sys 0m0.028s
```
### after
```console
$ time flake8 /dev/null
real 0m0.197s
user 0m0.182s
sys 0m0.012s
```
With a large number of errors, filenames, and per-file-ignores the style guide
selection would take a significant portion of execution time
(python3 70% / python2 99.9%).
Caching the styleguide lookup by filename eliminates this bottleneck.