Commit f32f2164 authored by Grant Paton-Simpson's avatar Grant Paton-Simpson

Add nested advisors; put repeated lines func in central location

parent a759cafb
git:
sed -i 's/^test_misc()/# test_misc()/' /home/g/projects/superhelp/tests/*.py
sed -i 's/RECORD_AST = t/RECORD_AST = f/' /home/g/projects/superhelp/superhelp/conf.py
sed -i 's/DEV_MODE = t/DEV_MODE = f/' /home/g/projects/superhelp/superhelp/conf.py
sed -i 's/DO_TEST = f/DO_TEST = t/' /home/g/projects/superhelp/superhelp/conf.py
......
......@@ -273,9 +273,10 @@ def short_name_check(block_dets, *, repeated_message=False):
going to be used or worked on in the future as opposed to quick
exploratory code in a terminal / notebook etc.
In the words of the legendary Donald Knuth "Programs are meant
to be read by humans and only incidentally for computers to
execute."
In the words of the legendary Donald Knuth:
> "Programs are meant to be read by humans
> and only incidentally for computers to execute."
Note - excessively long variable names can be unreadable as
well. They may also indicate the code needs to be reworked.
......
from ..advisors import filt_block_advisor
from .. import ast_funcs, conf
from ..utils import layout_comment as layout
FOR_XPATH = 'descendant-or-self::For'
WHILE_XPATH = 'descendant-or-self::While'
IF_XPATH = 'descendant-or-self::If' ## includes elif
OUTER_XPATHS = {
'for': FOR_XPATH,
'while': WHILE_XPATH,
'if': IF_XPATH,
}
NESTING_XPATH = ' | '.join(OUTER_XPATHS.values())
def _long_nested_block(outer_el):
_first_line_no, _last_line_no, el_lines_n = ast_funcs.get_el_lines_dets(
outer_el, ignore_trailing_lines=True)
nested_block_line_len = el_lines_n - 1 ## ignore the actual outer el e.g. for:
too_long = nested_block_line_len > conf.MAX_BRIEF_NESTED_BLOCK
return too_long
def has_long_block(block_el, xpath):
long_block = False
outer_els = block_el.xpath(xpath)
for outer_el in outer_els:
too_long = _long_nested_block(outer_el)
if too_long:
long_block = True
break
return long_block
@filt_block_advisor(xpath=NESTING_XPATH, warning=True)
def bloated_nested_block(block_dets, *, repeated_message=False):
"""
Look for long indented blocks under conditionals, inside loops etc that are
candidates for separating into functions to simplify the narrative of the
main code.
"""
bloated_outer_types = set()
for lbl, outer_xpath in OUTER_XPATHS.items():
if has_long_block(block_dets.element, outer_xpath):
bloated_outer_types.add(lbl)
if not bloated_outer_types:
return None
brief_comment = layout("""\
### Possible option of replacing long nested block with function call
""")
for bloated_outer_type in bloated_outer_types:
brief_comment += layout(f"""\
The code has at least one long nested block under
`{bloated_outer_type}:`
""")
if repeated_message:
main_comment = brief_comment
extra_comment = ''
else: ## Hypocrisy alert LOL - I complain here about excessive nested blocks
brief_comment += layout("""\
It may be possible to pull most of the nested code block into a
function which can be called instead.
""")
main_comment = brief_comment
main_comment += (
layout("""
For example, instead of:
""")
+
layout("""\
for name in names:
## contact name
line 1
line 2
line 3
...
line 30
logging.info("Finished!")
""", is_code=True)
+
layout("""\
you could possibly write:
""")
+
layout('''\
def contact(name):
"""
Contact person ...
"""
line 1
line 2
line 3
...
line 30
for name in names:
contact(name)
logging.info("Finished!")
''', is_code=True)
)
extra_comment = layout("""\
Computers can handle lots of nesting without malfunctioning. Human
brains are not so fortunate. As it says in The Zen of Python:
> "Flat is better than nested."
""")
message = {
conf.BRIEF: brief_comment,
conf.MAIN: main_comment,
conf.EXTRA: extra_comment,
}
return message
......@@ -26,20 +26,136 @@ else:
## When testing user-supplied snippets watch out for the BOM MS inserts via Notepad. AST chokes on it.
EXAMPLE_SNIPPET = """\
def sorted(my_list):
sorted_list = my_list.sort()
return sorted_list
"""
TEST_SNIPPET = """\
class Onsie:
def __init__(self):
pass
def one(self):
pass
# def two(self):
# pass
for i in range(2):
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
while True:
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
break
if 1 == 1:
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
elif 2 == 2:
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
pass
"""
DEMO_SNIPPET = """\
......@@ -221,6 +337,7 @@ MAX_BRIEF_FUNC_LOC = 35
MAX_BRIEF_FUNC_ARGS = 6
MIN_BRIEF_DOCSTRING = 3
MIN_BRIEF_NAME = 3
MAX_BRIEF_NESTED_BLOCK = 20
FUNCTION_LBL = 'function'
METHOD_LBL = 'method'
......
......@@ -397,6 +397,13 @@ li {
label {
font-size: 12px;
}
blockquote {
font-style: italic;
color: #666666;
}
blockquote p {
font-size: 13px;
}
svg {
height: 50px;
width: 60px;
......
......@@ -47,3 +47,17 @@ def check_as_expected(test_conf):
f"\n\nActual:\n{actual_source_freqs}"
)
)
def get_repeated_lines(*, item='pass', lpad=16, n_lines=100):
"""
:return: E.g.
'''pass
pass
pass
pass
pass
pass'''
:rtype: str
"""
repeated_lines = ('\n' + (lpad * ' ')).join([item]*n_lines)
return repeated_lines
from textwrap import dedent
from tests import check_as_expected
def get_repeated_lines(*, item='pass', lpad=16, n_lines=100):
"""
:return: E.g.
'''pass
pass
pass
pass
pass
pass'''
:rtype: str
"""
repeated_lines = ('\n' + (lpad * ' ')).join([item]*n_lines)
return repeated_lines
from tests import check_as_expected, get_repeated_lines
excess_args = ', '.join(['arg' + str(i) for i in range(100)])
......
from textwrap import dedent
from tests import check_as_expected, get_repeated_lines
ROOT = 'superhelp.advisors.nested_advisors.'
def test_misc():
test_conf = [
(
dedent("""\
pet = 'cat'
"""),
{
ROOT + 'bloated_nested_block': 0,
}
),
(
dedent("""\
if 1 == 1:
pass
"""),
{
ROOT + 'bloated_nested_block': 0,
}
),
(
dedent(f"""\
if 1 == 1:
{get_repeated_lines(item='pass', lpad=16, n_lines=40)}
"""),
{
ROOT + 'bloated_nested_block': 1,
}
),
(
dedent(f"""\
for i in range(2):
{get_repeated_lines(item='pass', lpad=16, n_lines=40)}
"""),
{
ROOT + 'bloated_nested_block': 1,
}
),
(
dedent(f"""\
while True:
{get_repeated_lines(item='pass', lpad=16, n_lines=40)}
break
"""),
{
ROOT + 'bloated_nested_block': 1,
}
),
(
dedent(f"""\
while True:
{get_repeated_lines(item='pass', lpad=16, n_lines=40)}
break
for i in range(2):
{get_repeated_lines(item='pass', lpad=16, n_lines=40)}
"""),
{
ROOT + 'bloated_nested_block': 2,
}
),
(
dedent(f"""\
while True:
{get_repeated_lines(item='pass', lpad=16, n_lines=2)}
break
for i in range(2):
{get_repeated_lines(item='pass', lpad=16, n_lines=40)}
"""),
{
ROOT + 'bloated_nested_block': 1,
}
),
(
dedent(f"""\
while True:
for i in range(2):
{get_repeated_lines(item='pass', lpad=16, n_lines=40)}
break
"""),
{
ROOT + 'bloated_nested_block': 1, ## consolidated message
}
),
]
check_as_expected(test_conf)
# test_misc()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment