1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
"""Command line utility for proselint."""
import json
import os
import shutil
import subprocess
import sys
import traceback
import click
from .config import default
from .tools import (close_cache_shelves, close_cache_shelves_after,
errors_to_json, lint, load_options)
from .version import __version__
CONTEXT_SETTINGS = {"help_option_names": ['-h', '--help']}
base_url = "proselint.com/"
proselint_path = os.path.dirname(os.path.realpath(__file__))
demo_file = os.path.join(proselint_path, "demo.md")
# TODO: fix broken corpus
def timing_test(corpus="0.1.0"):
"""Measure timing performance on the named corpus."""
import time
dirname = os.path.dirname
corpus_path = os.path.join(
dirname(dirname(os.path.realpath(__file__))), "corpora", corpus)
start = time.time()
for file in os.listdir(corpus_path):
filepath = os.path.join(corpus_path, file)
if ".md" == filepath[-3:]:
subprocess.call(["proselint", filepath, ">/dev/null"])
return time.time() - start
def clear_cache():
"""Delete the contents of the cache."""
click.echo("Deleting the cache...")
# see issue #624
_delete_cache()
def _delete_compiled_python_files():
"""Remove files with a 'pyc' extension."""
for path, _, files in os.walk(os.getcwd()):
for fname in [f for f in files if os.path.splitext(f)[1] == ".pyc"]:
try:
os.remove(os.path.join(path, fname))
except OSError:
pass
def _delete_cache():
"""Remove the proselint cache."""
proselint_cache = os.path.join("proselint", "cache")
try:
shutil.rmtree(proselint_cache)
except OSError:
pass
def print_errors(filename, errors, output_json=False, compact=False):
"""Print the errors, resulting from lint, for filename."""
if output_json:
click.echo(errors_to_json(errors))
else:
for error in errors:
(check, message, line, column, start, end,
extent, severity, replacements) = error
if compact:
filename = "-"
click.echo(
filename + ":" +
str(1 + line) + ":" +
str(1 + column) + ": " +
check + " " +
message)
@click.command(context_settings=CONTEXT_SETTINGS)
@click.version_option(__version__, '--version', '-v', message='%(version)s')
@click.option('--config', is_flag=False, type=click.Path(),
help="Path to configuration file.")
@click.option('--debug', '-d', is_flag=True, help="Give verbose output.")
@click.option('--clean', '-c', is_flag=True, help="Clear the cache.")
@click.option('--json', '-j', 'output_json', is_flag=True,
help="Output as JSON.")
@click.option('--time', '-t', is_flag=True, help="Time on a corpus.")
@click.option('--demo', is_flag=True, help="Run over demo file.")
@click.option('--compact', is_flag=True, help="Shorten output.")
@click.option('--dump-config', is_flag=True, help="Prints current config.")
@click.option('--dump-default-config', is_flag=True,
help="Prints default config.")
@click.argument('paths', nargs=-1, type=click.Path())
@close_cache_shelves_after
def proselint(paths=None, config=None, version=None, clean=None,
debug=None, output_json=None, time=None, demo=None, compact=None,
dump_config=None, dump_default_config=None):
"""Create the CLI for proselint, a linter for prose."""
if dump_default_config:
return print(json.dumps(default, sort_keys=True, indent=4))
config = load_options(config, default)
if dump_config:
print(json.dumps(config, sort_keys=True, indent=4))
return
if time:
# click.echo(timing_test())
print("This option does not work for the time being.")
return
# In debug or clean mode, delete cache & *.pyc files before running.
if debug or clean:
clear_cache()
# Use the demo file by default.
if demo:
paths = [demo_file]
# Expand the list of directories and files.
filepaths = extract_files(list(paths))
# Lint the files
num_errors = 0
# Use stdin if no paths were specified
if len(paths) == 0:
filepaths.append('-')
for fp in filepaths:
if fp == '-':
fp = '<stdin>'
f = sys.stdin
else:
try:
f = click.open_file(fp, 'r', "utf-8", "replace")
except Exception:
traceback.print_exc()
sys.exit(2)
errors = lint(f, debug, config)
num_errors += len(errors)
print_errors(fp, errors, output_json, compact)
# Return an exit code
close_cache_shelves()
if num_errors > 0:
sys.exit(1)
else:
sys.exit(0)
def extract_files(files):
"""Expand list of paths to include all text files matching the pattern."""
expanded_files = []
legal_extensions = [".md", ".txt", ".rtf", ".html", ".tex", ".markdown"]
for f in files:
# If it's a directory, recursively walk through it and find the files.
if os.path.isdir(f):
for dir_, _, filenames in os.walk(f):
for filename in filenames:
fn, file_extension = os.path.splitext(filename)
if file_extension in legal_extensions:
joined_file = os.path.join(dir_, filename)
expanded_files.append(joined_file)
# Otherwise add the file directly.
else:
expanded_files.append(f)
return expanded_files
if __name__ == '__main__':
proselint()
|