Commit 17b9443c authored by Grant Paton-Simpson's avatar Grant Paton-Simpson

Improve advisor docs; add ability to list advisor comments

parent 067d9f56
Pipeline #567 failed with stages
......@@ -2,39 +2,34 @@
Add advisors modules inside this folder.
To add more advice, just declare more advisor functions inside the advisors
modules with the @..._advisor decorators :-).
modules with the @..._advisor decorators :-). Make sure the first paragraph of
the docstring is a good, user-facing description of its purpose so it can be
automatically processed into lists of available advice in SuperHELP.
Advisors return None if a selected element doesn't match.
The basic pattern within an advisor function is:
WARNING: Dedenting can be broken by a single line which is not indented like the
rest. It is surprisingly easy to create this problem using new line characters
in your strings. So don't ;-). Use triple quotes and actual line breaks. Don't
believe me? See examples below:
* Get the correct elements and see if the target pattern is found e.g. a value
being assigned to a name
Note - the backslash after the triple quotes below is a standard Python trick to
prevent an extra implicit new line character.
* If not, exit returning None
E.g. dedent(
line 1
line 2
* Otherwise create all the different message parts ready to assemble in the
line 100\n Extra text FAIL - will not dedent beyond the one space before 'Extra text'.
Some parts will have two versions - one for the first time the message appears
for a block of the code snippet; and one for subsequent appearances. These
"repeat" versions are sometimes empty strings; other times they are simply
much shorter versions.
E.g. dedent(
line 1
\nline 2 ## FAIL - no indentation for line 2 so no dedenting possible
Don't manually try to dedent etc the message parts - use the layout_comment
function and follow the example of other advisor modules. It is very easy to
break markdown in ways which mess up the terminal output.
E.g. dedent(
line 1\n ## SUCCESS - lines 1 and 2 are indented and the new line character won't interfere
line 2
* Assemble the message. There can be up to three parts: brief, main, and extra.
Only the brief component is mandatory. If not supplied, the main component is
just a repeat of the brief component.
Subtle huh!!? And you thought whitespace in Python was a risk!
* Add to the appropriate test module. Only test advisors within the module.
import builtins
from collections import namedtuple
......@@ -45,7 +40,7 @@ import sys
from textwrap import dedent
from .. import conf
from ..utils import layout_comment as layout
from ..utils import get_docstring_start, layout_comment as layout
FILT_BLOCK_ADVISORS = [] ## block-based advisors which only apply to blocks filtered to contain specified element types
......@@ -140,10 +135,18 @@ def load_advisors():
for submodule in submodules:
def get_advisor_comments():
advisor_comments = []
all_advisors_dets = (
for advisor_dets in all_advisors_dets:
docstring = advisor_dets.advisor.__doc__
advisor_comment = get_docstring_start(docstring)
return advisor_comments
## =============================================================================
def is_reserved_name(name):
is_reserved = name in set(keyword.kwlist + dir(builtins) + conf.STD_LIBS)
return is_reserved
......@@ -413,4 +416,3 @@ SET_COMPREHENSION_COMMENT = (
......@@ -307,7 +307,7 @@ def selfless_methods(block_dets, *, repeat=False):
def one_method_classes(block_dets, *, repeat=False):
Look for classes with only one method (other than __init__) and suggest a
simple function as an alternative..
simple function as an alternative.
class_els = block_dets.element.xpath(CLASS_XPATH)
if not class_els:
......@@ -13,7 +13,7 @@ RECORD_AST = f ## (f)
DEV_MODE = f ## (f)
DO_TEST = t ## set test snippet as default rather than the larger demo snippet (t)
DO_HTML = t ## set html displayer as default (t)
DO_DISPLAYER = t ## f is only ever used when testing pre-display (t)
DO_DISPLAYER = f ## f is only ever used when testing pre-display (t)
## =============================================================================
......@@ -74,6 +74,8 @@ def get_advice(snippet=None, *, file_path=None, displayer='html',
message_level=message_level, in_notebook=in_notebook)
if in_notebook:
return res
for comment in advisors.get_advisor_comments():
def shelp():
......@@ -84,7 +84,7 @@ astpath.asts.convert_to_xml = convert_to_xml
## importing from superhelp only works properly after I've installed superhelp as a pip package (albeit as a link to this code using python3 -m pip install --user -e <path_to_proj_folder>)
## Using this as a library etc works with . instead of superhelp but I want to be be able to run the helper module from within my IDE
from superhelp import advisors, ast_funcs, conf # @UnresolvedImport
from superhelp.utils import layout_comment # @UnresolvedImport
from superhelp.utils import get_docstring_start, layout_comment # @UnresolvedImport
BlockDets = namedtuple(
'BlockDets', 'element, pre_block_code_str, block_code_str, first_line_no')
......@@ -179,7 +179,7 @@ def get_message_dets_from_input(advisor_dets, *,
Advisor {name} unable to run. Advisor description:
+ ## show first line of docstring (subsequent lines might have more technical, internally-oriented comments)
layout_comment(docstring.lstrip('\n').split('\n\n')[0] + '\n')
layout_comment(get_docstring_start(docstring) + '\n')
......@@ -79,3 +79,8 @@ def layout_comment(raw_comment, *, is_code=False):
comment = '\n' + '\n\n'.join(new_paragraphs)
return comment
def get_docstring_start(docstring):
docstring_start = (
docstring.lstrip('\n').split('\n\n')[0].strip().replace('\n ', ' '))
return docstring_start
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment