From 8a05f37131bf0879604f6d4652104614f068d26d Mon Sep 17 00:00:00 2001 From: Matthew Gamble Date: Sun, 8 Aug 2021 13:30:05 +1000 Subject: [PATCH 1/2] Update argument handling - Start the migration to only using namespaced flag names. - Log deprecation warnings for anyone relying on the old behaviour. - Take advantage of argparse functionality in the new flags, because flake8 has migrated away from optparse. - Update readme accordingly # Conflicts: # flake8_spellcheck/__init__.py --- README.rst | 80 +++++++++----- flake8_spellcheck/__init__.py | 126 +++++++++++++++++++--- whitelist.txt => spellcheck-allowlist.txt | 0 3 files changed, 167 insertions(+), 39 deletions(-) rename whitelist.txt => spellcheck-allowlist.txt (100%) diff --git a/README.rst b/README.rst index 96bba48..d5695a4 100644 --- a/README.rst +++ b/README.rst @@ -6,71 +6,103 @@ Flake8 Spellcheck Flake8 Plugin that spellchecks variables, functions, classes and other bits of your python code. -You can whitelist words that are specific to your project simply by adding them to ``whitelist.txt`` -in the root of your project directory. Each word you add should be separated by a newline. +You can whitelist words that are specific to your project simply by adding them to ``spellcheck-allowlist.txt`` +in the root of your project directory. Each word you add should be separated by a newline. Spelling is assumed to be in en_US. This plugin supports python 3.6+ Codes ------ +===== * SC100 - Spelling error in comments * SC200 - Spelling error in name (e.g. variable, function, class) -Enable Django support ---------------------- +Configuration +============= -You can enable support for a Django dictionary by adding the following to your -flake8 configuration (e.g. your ``.flake8`` file): +Enable non-default dictionaries +------------------------------- -.. code-block:: ini +This plugin comes with a couple of dictionary files that aren't enabled by default. + +- ``django`` +- ``pytest`` + +To enable a custom dictionary, you can use the ``--spellcheck-add-dictionary`` flag. + +.. code-block:: + + flake8 --spellcheck-add-dictionary django --spellcheck-add-dictionary pytest mypackage - [flake8] - dictionaries=en_US,python,technical,django +Disable default dictionaries +---------------------------- -Enable pandas support ---------------------- +The default dictionary list is as follows: -You can enable support for pandas DataFrames by adding the following to your -flake8 configuration (e.g. your ``.flake8`` file): +- ``en_US`` +- ``technical`` +- ``python`` + +You can disable the default dictionary list, if you want to be highly specific about the wordlist in use. + +.. code-block:: + + flake8 --spellcheck-disable-default-dictionaries mypackage + +Or in your flake8 configuration (e.g. your ``.flake8`` file): .. code-block:: ini - [flake8] - dictionaries=en_US,python,technical,pandas + spellcheck-disable-default-dictionaries = true + +You can then enable built-in dictionaries one-by-one as necessary. +.. code-block:: -Specify Targets ---------------- + flake8 --spellcheck-disable-default-dictionaries --spellcheck-add-dictionary python --spellcheck-add-dictionary pytest --spellcheck-add-dictionary pandas mypackage + +Or in your flake8 configuration file: + +.. code-block:: ini + + spellcheck-add-dictionary = python,pytest,pandas + +Specify Code Targets +-------------------- Both ``comments`` and ``names`` (variable names, function names...) are spellchecked by default. -You can specify what targets to spellcheck in your flake8 configuration (e.g. in your ``.flake8`` file): +You can specify what targets to spellcheck by specifying the ``--spellcheck-targets`` flag. + +.. code-block:: + + flake8 --spellcheck-targets comments mypackage + +Or by setting the ``spellcheck-targets`` option in your flake8 configuration (e.g. in your ``.flake8`` file). .. code-block:: ini [flake8] spellcheck-targets=comments -The above configuration would only spellcheck comments +The above configuration would only spellcheck comments, while this configuration will only check symbol names. .. code-block:: ini [flake8] spellcheck-targets=names -The above configuration would only spellcheck names - Contributing ------------- +============ If you have found word(s) which are listed as a spelling error but are actually correct terms used in python or in technical implementations (e.g. http), then you can very easily contribute by adding those word(s) to the appropriate dictionaries: * `python dictionary `_ -* `technical dictionary `_ +* `pytest dictionary `_ +* `general technical dictionary `_ * `django dictionary `_ * `pandas dictionary `_ diff --git a/flake8_spellcheck/__init__.py b/flake8_spellcheck/__init__.py index 7dce480..9ba4568 100644 --- a/flake8_spellcheck/__init__.py +++ b/flake8_spellcheck/__init__.py @@ -1,13 +1,32 @@ -import os +import enum +import logging import re import sys import tokenize +from argparse import Namespace +from itertools import chain +from pathlib import Path from string import ascii_lowercase, ascii_uppercase, digits +from typing import Any, FrozenSet, Iterable, Iterator, List, Optional, Tuple, Type + +from flake8.options.manager import OptionManager from .version import version as __version__ + +logger = logging.getLogger(__name__) + + NOQA_REGEX = re.compile(r"#[\s]*noqa:[\s]*[\D]+[\d]+") +DICTIONARY_PATH = Path(__file__).parent +DEFAULT_DICTIONARY_NAMES = ("en_US", "python", "technical") + + +LintError = Tuple[int, int, str, Type["SpellCheckPlugin"]] +Position = Tuple[int, int] + + if sys.version_info >= (3, 7): from importlib.resources import read_text else: @@ -86,6 +105,48 @@ def get_code(token_type): raise ValueError("Unknown token_type {}".format(token_type)) +def find_allowlist_path(options: Namespace) -> Optional[Path]: + if options.spellcheck_allowlist: + if options.spellcheck_allowlist.exists(): + return options.spellcheck_allowlist + else: + logger.error("ERROR: Supplied allowlist file for flake8-spellcheck does not exist.") + return None + elif options.whitelist: + logger.warning( + "DEPRECATED: Support for '--whitelist' will be removed in future. Please use '--spellcheck-allowlist' instead." + ) + whitelist_path = Path(options.whitelist) + if whitelist_path.exists(): + return whitelist_path + else: + logger.error("ERROR: Supplied allowlist file for flake8-spellcheck does not exist.") + return None + + default_allowlist_path = Path("spellcheck-allowlist.txt") + if default_allowlist_path.exists(): + return default_allowlist_path + + default_whitelist_path = Path("whitelist.txt") + if default_whitelist_path.exists(): + logger.warning( + "DEPRECATED: Support for 'whitelist.txt' will be removed in future. Please use 'spellcheck-allowlist.txt' instead." + ) + return default_whitelist_path + + return None + + +def flatten_dictionary_add_list(options: Namespace) -> Iterator[str]: + for identifier in options.spellcheck_add_dictionary: + if isinstance(identifier, str): + yield identifier + continue + + for _id in identifier: + yield _id + + class SpellCheckPlugin: name = "flake8-spellcheck" version = __version__ @@ -96,14 +157,32 @@ def __init__(self, tree, filename="(none)", file_tokens=None): @classmethod def load_dictionary(cls, options): words = set() - for dictionary in ("{}.txt".format(d) for d in options.dictionaries): - data = read_text(__name__, dictionary) - words |= set(w.lower() for w in data.split("\n")) - if os.path.exists(options.whitelist): - with open(options.whitelist, "r") as fp: - whitelist = fp.read() - whitelist = set(w.lower() for w in whitelist.split("\n")) - words |= whitelist + + dictionary_names: Iterable[str] + if options.dictionaries: + logger.warning( + "DEPRECATED: Support for '--dictionaries' will be removed in future. Use '--spellcheck-add-dictionary' instead." + ) + dictionary_names = options.dictionaries + else: + if options.spellcheck_disable_default_dictionaries: + dictionary_names = flatten_dictionary_add_list(options) + else: + dictionary_names = chain(DEFAULT_DICTIONARY_NAMES, flatten_dictionary_add_list(options)) + + for dictionary_name in dictionary_names: + dictionary_path = DICTIONARY_PATH / "{}.txt".format(dictionary_name) + if dictionary_path.exists(): + dictionary_data = dictionary_path.read_text() + words |= set(word.lower() for word in dictionary_data.split("\n")) + else: + logger.error("ERROR: Supplied built-in dictionary '{}' does not exist.".format(dictionary_name)) + + allowlist_path: Optional[Path] = find_allowlist_path(options) + if allowlist_path: + allowlist_data = allowlist_path.read_text() + words |= set(w.lower() for w in allowlist_data.split("\n")) + # Hacky way of getting dictionary with symbols stripped no_symbols = set() for w in words: @@ -115,21 +194,38 @@ def load_dictionary(cls, options): @classmethod def add_options(cls, parser): + parser.add_option( + "--spellcheck-allowlist", + help="Path to text file containing a custom list of allowed words.", + type=Path, + parse_from_config=True, + ) parser.add_option( "--whitelist", - help="Path to text file containing whitelisted words", - default="whitelist.txt", + help="(deprecated) Path to text file containing a custom list of allowed words. Use '--spellcheck-allowlist' instead.", + parse_from_config=True, + ) + parser.add_option( + "--spellcheck-disable-default-dictionaries", + help="Don't use the default list of built-in dictionaries. You can still use '--spellcheck-add-dictionary' to select individual built-in dictionaries.", + action="store_true", parse_from_config=True, ) + parser.add_option( + "--spellcheck-add-dictionary", + help="A built-in dictionary to enable. Pass this flag multiple times to enable multiple dictionaries.", + action="append", + default=[], + parse_from_config=True, + comma_separated_list=True, + ) parser.add_option( "--dictionaries", - # Unfortunately optparse does not support nargs="+" so we - # need to use a command separated list to work round it - help="Command separated list of dictionaries to enable", - default="en_US,python,technical", + help="(deprecated) A comma-separated list of built-in dictionaries to enable. Use '--spellcheck-add-dictionary' instead.", comma_separated_list=True, parse_from_config=True, ) + # TODO: Convert this to use action=append parser.add_option( "--spellcheck-targets", help="Specify the targets to spellcheck", diff --git a/whitelist.txt b/spellcheck-allowlist.txt similarity index 100% rename from whitelist.txt rename to spellcheck-allowlist.txt From 28b4c8d6910afdb2af5c0e19cc3505ebaeb5305b Mon Sep 17 00:00:00 2001 From: Sean 'Shaleh' Perry Date: Mon, 28 Mar 2022 09:44:55 -0700 Subject: [PATCH 2/2] Update wording. --- README.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 5d5f1dc..a361064 100644 --- a/README.rst +++ b/README.rst @@ -6,8 +6,7 @@ Flake8 Spellcheck Flake8 Plugin that spellchecks variables, functions, classes and other bits of your python code. -You can whitelist words that are specific to your project simply by adding them to ``spellcheck-allowlist.txt`` -in the root of your project directory. Each word you add should be separated by a newline. +Words specific to your project are supported by adding them to ``spellcheck-allowlist.txt`` in the root of your project directory. Each word you add should be separated by a newline. Spelling is assumed to be in en_US.