Skip to content
This repository has been archived by the owner on Mar 8, 2024. It is now read-only.

Commit

Permalink
adding nolint, nodoc, and todo tags, as well as --stats for counting …
Browse files Browse the repository at this point in the history
…up totals. cleaning up tags models as well
  • Loading branch information
jpetrucciani committed Sep 13, 2019
1 parent e46f435 commit 061592f
Show file tree
Hide file tree
Showing 7 changed files with 212 additions and 43 deletions.
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,30 @@ pip install archives
# run archives (on itself!)
archives archives/

#> archives.py:846:0: F104 function 'path_empty' missing @ret tag
#>
#> Impossible! Perhaps your archives are incomplete?
#> 1 issues found.
# archives.py:846:0: F104 function 'path_empty' missing @ret tag
#
# Impossible! Perhaps your archives are incomplete?
# 1 issues found.

# list tags!
archives --list-tag

# @arg describe an argument of a function
# @author denote the author of a module/class/function
# @cc denote the complexity of a function
# @desc describe a module/class/function
# @link add a link to the generated documentation
# @nodoc disable this module/class/function in the documentation
# @nolint disable archives linting in this module/class/function
# @note add a note to a module/class/function
# @ret describe the return value of a function
# @todo tag something as a todo
# @warn add a warning to a module/class/function

# list rules!
archives --list-rules


# disable rules!
archives --disable M100 .

Expand Down
63 changes: 59 additions & 4 deletions archives/archives.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
)
from archives.models.python import Class, Function, Module
from archives.models.rules import Issue
from archives.models.tags import Tags, CHAR
from archives.utils.state import get_state, State
from archives.utils.files import (
find_project_root,
Expand Down Expand Up @@ -65,14 +66,20 @@ def parse_module(filename: str) -> Module:

def function_lint(function: Function) -> List:
"""
@cc 7
@cc 8
@desc function specific lint
@arg function: the Function object to lint
@ret a list of issues found in this function
"""
state = get_state()
issues = []

state.function_count += 1

if function.doc and function.doc.no_lint:
state.function_nolint_count += 1
return []

# check this function for rules
for rule in state.function_rules:
if rule.check(function):
Expand Down Expand Up @@ -108,7 +115,7 @@ def function_lint(function: Function) -> List:

def class_lint(class_def: Class) -> List:
"""
@cc 4
@cc 5
@desc class specific lint
@arg class_def: the Class object to lint
@ret a list of issues found in this class
Expand All @@ -117,6 +124,12 @@ def class_lint(class_def: Class) -> List:
state = get_state()
issues = []

state.class_count += 1

if class_def.doc and class_def.doc.no_lint:
state.class_nolint_count += 1
return []

# check this class for rules
for rule in state.class_rules:
if rule.check(class_def):
Expand All @@ -135,7 +148,7 @@ def class_lint(class_def: Class) -> List:

def lint(module: Module) -> List:
"""
@cc 4
@cc 5
@desc lint the given module!
@arg module: the module to lint
@ret a list of issues found in this module
Expand All @@ -144,6 +157,12 @@ def lint(module: Module) -> List:
issues = []
state = get_state()

state.module_count += 1

if module.doc and module.doc.no_lint:
state.module_nolint_count += 1
return []

for rule in state.module_rules:
if rule.check(module):
issues.append(Issue(rule, module))
Expand Down Expand Up @@ -215,6 +234,20 @@ def archives_lint(ctx: click.Context, sources: Set[Path], state: State) -> None:
)
out(f"0 issues found", color="blue")

if state.stats:
_mods = state.module_count
_cls = state.class_count
_fns = state.function_count
out(
f"{_mods} module{'s' if _mods != 1 else ''} ({state.module_nolint_count} nolint)"
)
out(
f"{_cls} class{'es' if _cls != 1 else ''} ({state.class_nolint_count} nolint)"
)
out(
f"{_fns} function{'s' if _fns != 1 else ''} ({state.function_nolint_count} nolint)"
)

ctx.exit(0 if not issues else 1)


Expand Down Expand Up @@ -269,6 +302,13 @@ def archives_doc(ctx: click.Context, sources: Set[Path], state: State) -> None:
is_eager=True,
help="list all active rules",
)
@click.option(
"--list-tags",
is_flag=True,
default=False,
is_eager=True,
help="list all active tags",
)
@click.option(
"--doc",
is_flag=True,
Expand All @@ -281,6 +321,12 @@ def archives_doc(ctx: click.Context, sources: Set[Path], state: State) -> None:
default=False,
help="ignore parsing exceptions (useful for ci)",
)
@click.option(
"--stats",
is_flag=True,
default=False,
help="print out additional stats for this linting run",
)
@click.version_option(version=__version__)
@click.argument(
"src",
Expand All @@ -300,14 +346,16 @@ def archives(
format: str,
disable: str,
list_rules: bool,
list_tags: bool,
stats: bool,
ignore_exceptions: bool,
doc: bool,
src: Tuple[str],
) -> None:
"""
check if your code's archives are incomplete!
\f
@cc 7
@cc 8
@desc the main cli method for archives
@arg ctx: the click context arg
@arg quiet: the cli quiet flag
Expand All @@ -317,6 +365,8 @@ def archives(
@arg format: a flag to specify output format for the issues
@arg disable: a comma separated disable list for rules
@arg list_rules: a flag to print the list of rules and exit
@arg list_tags: a flag to print the list of tags and their descriptions
@arg stats: a flag to print extra stats at the end of a lint run
@arg ignore_exceptions: a flag to ignore parsing errors and exit 0
@arg doc: a flag to specify if we should generate docs instead of lint
@arg src: a file or directory to scan for files to lint
Expand All @@ -327,6 +377,7 @@ def archives(
state.format = format
state.disable_list = disable.split(",")
state.ignore_exceptions = ignore_exceptions
state.stats = stats

if list_rules:
for rule in [
Expand All @@ -339,6 +390,10 @@ def archives(
]:
out(f"{rule.code}: {rule.desc}")
ctx.exit(0)
if list_tags:
for tag in Tags.all():
out(f"{CHAR}{tag.name}\t{tag.desc}")
ctx.exit(0)
try:
include_regex = re.compile(include)
except re.error:
Expand Down
2 changes: 1 addition & 1 deletion archives/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typed_ast import ast3 # noqa


__version__ = "0.9"
__version__ = "0.10"


PY_VERSION = sys.version_info
Expand Down
37 changes: 26 additions & 11 deletions archives/models/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import Dict, Set, Union
from archives.globals import ast3, DEFAULT_ARG_IGNORE
from archives.utils.text import debug
from archives.models.tags import Tag
from archives.models.tags import Tags


def parse_elt(elt: Union[ast3.Name, ast3.Subscript]) -> str:
Expand Down Expand Up @@ -93,22 +93,30 @@ def __init__(self, doc_string: ast3.Expr, doc_type: Type) -> None:
@arg doc_type: the enum type of doc string this is used for
"""
self.value = doc_string.value.s.strip() # type: ignore
desc = Tag.DESC.search(self.value)
ret = Tag.RETURN.search(self.value)
cc = Tag.CC.search(self.value)
author = Tag.AUTHOR.search(self.value)
desc = Tags.DESC.regex.search(self.value)
ret = Tags.RETURN.regex.search(self.value)
cc = Tags.CC.regex.search(self.value)
author = Tags.AUTHOR.regex.search(self.value)
todo = Tags.TODO.regex.search(self.value)

self.no_lint = bool(Tags.NO_LINT.regex.search(self.value))
self.no_doc = bool(Tags.NO_DOC.regex.search(self.value))
self.todo = todo[1] if todo else ""

self.desc = desc[1] if desc else ""
self.args = {
x: y for x, y in Tag.ARG.findall(self.value) if x not in DEFAULT_ARG_IGNORE
}
self.links = {
x: y for x, y in Tag.LINK.findall(self.value) if x not in DEFAULT_ARG_IGNORE
x: y
for x, y in Tags.ARG.regex.findall(self.value)
if x not in DEFAULT_ARG_IGNORE
}
self.links = {x: y for x, y in Tags.LINK.regex.findall(self.value)}
self.ret = ret[1] if ret else ""
self.author = author[1] if author else ""
self.cc = int(cc[1] if cc else -1)

self.notes = {x: y for x, y in Tags.NOTE.regex.findall(self.value)}
self.warnings = {x: y for x, y in Tags.WARN.regex.findall(self.value)}

def __repr__(self) -> str:
"""
@cc 1
Expand All @@ -130,6 +138,9 @@ def serialize(self) -> Dict:
author=self.author,
links=self.links,
args=self.args,
notes=self.notes,
warnings=self.warnings,
no_lint=self.no_lint,
)


Expand All @@ -143,7 +154,6 @@ def __init__(self, arg: ast3.arg) -> None:
@cc 2
@desc easier to use version of the ast arg def
@arg arg: the AST arg object to parse
"""
self.typed = False
self.line = arg.lineno
Expand Down Expand Up @@ -256,10 +266,13 @@ def serialize(self) -> Dict:
@ret a dict of this arg's properties
"""
return dict(
author=self.doc.author if self.doc else None,
name=self.name,
line=self.line,
column=self.column,
args=[x.serialize() for x in self.args],
functions=[x.serialize() for x in self.functions],
classes=[x.serialize() for x in self.classes],
complexity=self.complexity,
returns=self.returns,
doc=self.doc.serialize() if self.doc else None,
Expand All @@ -275,7 +288,7 @@ def __init__(self, cls: ast3.ClassDef, module: "Module") -> None:
"""
@cc 2
@desc easier to use version of a class
@arg cls: the AST classDef to parse
@arg cls: the AST ClassDef to parse
@arg module: the module this class resides in
"""
self.body = cls.body
Expand Down Expand Up @@ -312,6 +325,7 @@ def serialize(self) -> Dict:
@ret a dict of this arg's properties
"""
return dict(
author=self.doc.author if self.doc else None,
name=self.name,
line=self.line,
column=self.column,
Expand Down Expand Up @@ -362,6 +376,7 @@ def serialize(self) -> Dict:
@ret a dict of this arg's properties
"""
return dict(
author=self.doc.author if self.doc else None,
name=self.name,
functions=[x.serialize() for x in self.functions],
classes=[x.serialize() for x in self.classes],
Expand Down
Loading

0 comments on commit 061592f

Please sign in to comment.