Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update argument handling #66

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 55 additions & 24 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,71 +6,102 @@ 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.
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.

This plugin supports python 3.8+

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 <flake8_spellcheck/python.txt>`_
* `technical dictionary <flake8_spellcheck/technical.txt>`_
* `pytest dictionary <flake8_spellcheck/pytest.txt>`_
* `general technical dictionary <flake8_spellcheck/technical.txt>`_
* `django dictionary <flake8_spellcheck/django.txt>`_
* `pandas dictionary <flake8_spellcheck/pandas.txt>`_

Expand Down
117 changes: 102 additions & 15 deletions flake8_spellcheck/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import enum
import importlib.metadata
import logging
import os
import re
import tokenize
from argparse import Namespace
from ast import AST
from itertools import chain
from pathlib import Path
from string import ascii_lowercase, ascii_uppercase, digits
from tokenize import TokenInfo
from typing import Any, FrozenSet, Iterable, Iterator, List, Optional, Tuple, Type

from flake8.options.manager import OptionManager

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"]]
Expand Down Expand Up @@ -98,6 +105,48 @@ def get_code(token_type: int) -> str:
raise ValueError(f"Unknown token_type {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 = importlib.metadata.version(__name__)
Expand All @@ -120,16 +169,37 @@ def __init__(
@classmethod
def load_dictionaries(cls, options: Namespace) -> Tuple[FrozenSet[str], FrozenSet[str]]:
words = set()
for dictionary_name in options.dictionaries:
dictionary_path = DICTIONARY_PATH / f"{dictionary_name}.txt"
data = dictionary_path.read_text()
words |= {w.lower() for w in data.split("\n")}

if os.path.exists(options.whitelist):
with open(options.whitelist) as fp:
whitelist = fp.read()
whitelist_data = {w.lower() for w in whitelist.split("\n")}
words |= whitelist_data
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()
Expand All @@ -142,21 +212,38 @@ def load_dictionaries(cls, options: Namespace) -> Tuple[FrozenSet[str], FrozenSe

@classmethod
def add_options(cls, parser: OptionManager) -> None:
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",
Expand Down
File renamed without changes.