Skip to content

Commit

Permalink
Add prototype plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
calvertvl committed Aug 8, 2022
1 parent b9e7aa1 commit fc6c46a
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 16 deletions.
24 changes: 8 additions & 16 deletions flake8p/hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
import flake8.main.cli
import flake8.options.config
import sys
if sys.version_info >= (3, 11):
import tomllib as toml
else:
import tomli as toml
from pathlib import Path

from flake8p.util import load_flake8_from_toml, find_and_load_toml_file, normalize_from_toml


# Remember original Flake8 objects.
flake8_RawConfigParser = flake8.options.config.configparser.RawConfigParser
Expand All @@ -23,29 +21,23 @@ class RawConfigParser(flake8_RawConfigParser):
def _read(self, stream, path):
file = Path(path)
if file.name == 'pyproject.toml':
with file.open('rb') as f:
settings = toml.load(f)
_, settings = load_flake8_from_toml(file)
if not self.has_section('flake8'):
self.add_section('flake8')
for (key, value) in settings['tool']['flake8'].items():
if isinstance(value, (bool, int, float)):
value = str(value)
settings = normalize_from_toml(settings)
for (key, value) in settings['flake8'].items():
self.set('flake8', key, value)
else:
super()._read(stream, path)


def find_config_file(path):
"""Convinces Flake8 to prefer `pyproject.toml` over other config files."""
file = Path(path)/'pyproject.toml'
if file.exists():
with file.open('rb') as f:
settings = toml.load(f)
if 'tool' in settings and 'flake8' in settings['tool']:
return str(file)
config_path, config = find_and_load_toml_file(path)
if config_path and config:
return str(config_path)
return flake8_find_config_file(path)


# Monkey-patch Flake8 with our modified objects.
flake8.options.config.configparser.RawConfigParser = RawConfigParser
flake8.options.config._find_config_file = find_config_file
Expand Down
19 changes: 19 additions & 0 deletions flake8p/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from typing import Any, Dict, List
import configparser

from flake8p.util import monkeypatch

import flake8.options.config

class FlakeConfigToml:
def __init__(self, tree):
pass

def __iter__(self):
raise StopIteration()

def run(self, *args) -> List[Any]:
return []

def add_options(self, *args):
monkeypatch()
80 changes: 80 additions & 0 deletions flake8p/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from typing import Tuple, Any, Dict, List, Optional
from pathlib import Path
import configparser

if sys.version_info >= (3, 11):
import tomllib as toml
else:
import tomli as toml

import flake8.options.config

def _stat_key(p: Path) -> Tuple[int, int]:
st = p.stat()
return st.st_ino, st.st_dev

def load_flake8_from_toml(filename: Path) -> Dict[str, Any]:
try:
with filename.open('rb') as f:
settings = toml.load(f)
if 'tool' in settings and 'flake8' in settings['tool']:
return {"flake8": settings["tool"]["flake8"]}
else:
return {}
except Exception:
return {}

def normalize_from_toml(settings: Dict[str, Any]) -> Dict[str, Any]:
output = {}
for key, value in settings["flake8"].items():
if isinstance(value, (bool, int, float)):
value = str(value)
elif isinstance(value, list):
value = ",".join(str(x) for x in value)
output[key] = value
return {"flake8": output}

def find_and_load_toml_file(_path: Optional[str]) -> Tuple[Optional[Path], Dict[str, Any]]:
if _path:
path = Path(_path).resolve()
else:
path = Path(".").resolve()
try:
# if the homedir isn't detected expanduser() raises RuntimeError
home_stat = _stat_key(Path("~").expanduser())
except RuntimeError:
home_stat = None

dir_stat = _stat_key(path)
while True:
cfg_path = path / "pyproject.toml"
if cfg_path.exists():
cfg = load_flake8_from_toml(cfg_path)
if cfg:
return cfg_path, cfg
new_path = path.parent
new_dir_stat = _stat_key(new_path)
if new_dir_stat == dir_stat or new_dir_stat == home_stat:
break
else:
path = new_path
dir_stat = new_dir_stat

# did not find any configuration file
return None, {}

def monkeypatch():
flake8_parse_config = flake8.options.config.parse_config
flake8_option_manager = flake8.options.config.OptionManager

def parse_config(
option_manager: flake8_option_manager,
cfg: configparser.RawConfigParser,
cfg_dir: str,
) -> Dict[str, Any]:
loaded_toml = find_and_load_toml_file()
normalized = normalize_from_toml(loaded_toml)
cfg.read_dict(normalized)
return flake8_parse_config(option_manager, cfg, cfg_dir)

flake8.options.config.parse_config = parse_config
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ dependencies = [
[project.scripts]
flake8p = 'flake8p:main'

[project.entry-points."flake8.extensions"]
CFG999 = "flake8p.plugin:FlakeConfigToml"

[project.optional-dependencies]
test = ['pyTest', 'pyTest-cov']

Expand Down

0 comments on commit fc6c46a

Please sign in to comment.