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

Add print advisors; misc

* Add print advisors
* Linter advisor handles unused imports and undefined names more usefully
* Streamline lint configuration
* Strip new lines off outsides of snippets
* Tweak formatting of string messages
* Update notebook
* Bump version
parent 5e207b8c
Pipeline #576 failed with stages
# https://git.nzoss.org.nz/pyGrant/superhelp
version number: 0.9.14
version number: 0.9.15
author: Grant Paton-Simpson
## Overview
......
This diff is collapsed.
......@@ -2,7 +2,7 @@ from setuptools import setup, find_packages # @UnresolvedImport
from codecs import open
from os import path
__version__ = '0.9.14'
__version__ = '0.9.15'
here = path.abspath(path.dirname(__file__))
......
......@@ -363,14 +363,13 @@ def one_method_classes(block_dets, *, repeat=False):
structure can make it easier to test intermediate state rather than
just function outputs. So, as with most things, it depends.
""")
function_demo = (
layout(f"""\
function_option = layout(f"""\
It may be simpler to replace the class{class_plural} with simple
functions e.g. we could replace:
It may be simpler to replace the class{class_plural} with simple
functions.
""")
+
function_demo = (
layout('''\
class GetSquare:
......@@ -407,10 +406,12 @@ def one_method_classes(block_dets, *, repeat=False):
)
else:
not_just_oo = ''
function_option = ''
function_demo = ''
message = {
conf.BRIEF: summary + n_methods_msg + function_demo,
conf.BRIEF: summary + n_methods_msg + function_option,
conf.MAIN: summary + n_methods_msg + function_option + function_demo,
conf.EXTRA: not_just_oo,
}
return message
......@@ -284,10 +284,7 @@ def lint_snippet(snippet):
""")
findings = layout("""\
Here is what the linter reported about your snippet. Note - if your
snippet is taken from a broader context the linter might be concerned
about names it doesn't know about, variables not used (yet) etc - i.e.
there may be some unavoidable false alarms.
Here is what the linter reported about your snippet.
""")
brief_msg, main_msg, extra_msg = get_lint_messages_by_level(
......
from ..advisors import all_blocks_advisor
from .. import conf
from ..utils import layout_comment as layout
def _includes_print(block_el):
"""
print('Hi') is Call/func/Name id = 'print'
p = print is Assign/value/Name id = 'print'
"""
func_name_els = block_el.xpath('descendant-or-self::Call/func/Name')
print_els = [func_name_el for func_name_el in func_name_els
if func_name_el.get('id') == 'print']
if print_els:
return True
assigned_name_els = block_el.xpath('descendant-or-self::Assign/value/Name')
print_assigned_els = [
assigned_name_el for assigned_name_el in assigned_name_els
if assigned_name_el.get('id') == 'print']
if print_assigned_els:
return True
return False
@all_blocks_advisor()
def print_overview(blocks_dets, *, repeat=False):
"""
Show some of the surprise features of the humble print function.
"""
has_print = False
for block_dets in blocks_dets:
if _includes_print(block_dets.element):
has_print = True
break
if not has_print:
return None
title = layout("""\
### `print` function used
""")
if not repeat:
brief_details = layout("""\
`print` may seem like a very basic function but it has some extra
parameters providing extra functionality. More information can be
found in the official on-line documentation for `print`.
""")
main_details = (
layout("""\
`print` may seem like a very basic function but it has some
extra features worth knowing about. Here is an example using the
`end` and `flush` parameters. Dots should appear one by one in
the same line. If `flush` were `False` then they would only
appear at the end all at once. If `end` were the default new
line character '\\n' they would have appeared on separate lines.
""")
+
layout("""\
from time import sleep
print('.', end='', flush=True)
sleep(0.25)
""", is_code=True)
+
layout("""\
More information can be found in `print`'s official on-line
documentation.
""")
)
else:
brief_details = ''
main_details = ''
message = {
conf.BRIEF: title + brief_details,
conf.MAIN: title + main_details,
}
return message
......@@ -115,35 +115,36 @@ def assigned_str_overview(block_dets, *, repeat=False):
longer_demo = layout(f"""\
Examples:
{name2use}.upper() returns {val2use.upper()}.
`{name2use}.upper()` returns {val2use.upper()}.
{name2use}.center(70, '=') returns {val2use.center(70, '=')}
`{name2use}.center(70, '=')` returns {val2use.center(70, '=')}
{name2use}.endswith('chicken') returns {val2use.endswith('chicken')}
`{name2use}.endswith('chicken')` returns
{val2use.endswith('chicken')}
{name2use} + ' is a string' returns {val2use + ' is a string'}
`{name2use}` + ' is a string' returns {val2use + ' is a string'}
{name2use} + ' ' + '{{\\NBLACK HEART}}' + ' Python' returns
`{name2use}` + ' ' + '{{\\NBLACK HEART}}' + ' Python' returns
{val2use + ' ' + black_heart + ' Python'}
len({name2use}) returns {len(val2use)} because that is how many
characters are in the {name2use} string (remember to count spaces -
they are characters too)
`len({name2use})` returns {len(val2use)} because that is how many
characters are in the `{name2use}` string (remember to count spaces
- they are characters too)
sorted({name2use}) returns {sorted(val2use)}
`sorted({name2use})` returns {sorted(val2use)}
""")
more = layout("""\
.upper(), .center() etc are abilities available with all Python
`.upper()`, `.center()` etc are abilities available with all Python
strings. Technically they are methods of string objects. They start
with a dot and are on the end of the object.
To see the full list of string methods enter dir(str) into a Python
command line.
To see the full list of string methods enter `dir(str)` into a
Python command line.
len() is a function which can be used on lots of things - not just
`len()` is a function which can be used on lots of things - not just
string objects. It is not a method of the string object. Other
functions that are not string-specific but are commonly used with
strings include sorted() and print().
strings include `sorted()` and `print()`.
""")
else:
cool = ''
......@@ -170,6 +171,7 @@ def str_combination(combination_type, str_els, *, repeat=False):
title = layout("""\
### Strings created by combining or interpolating strings
""")
how_combined_bits = []
for str_el in str_els:
......@@ -183,7 +185,7 @@ def str_combination(combination_type, str_els, *, repeat=False):
combination_comment = combination_type2comment[combination_type]
how_combined_bits.append(layout(f"""\
{name} is created using {combination_comment}.
`{name}` is created using {combination_comment}.
"""))
how_combined = ''.join(how_combined_bits)
if not repeat:
......@@ -205,7 +207,7 @@ def str_combination(combination_type, str_els, *, repeat=False):
f-strings let you reference variables from earlier in your
code and allow very readable string construction. All the
usual tricks of the .format() approach also work. For
usual tricks of the `.format()` approach also work. For
example, comma separating thousands in numbers:
""")
......
......@@ -26,13 +26,10 @@ else:
## When testing user-supplied snippets watch out for the BOM MS inserts via Notepad. AST chokes on it.
## All snippets here should be raw strings (see https://stackoverflow.com/questions/53636723/python-parsing-code-with-new-line-character-in-them-using-ast)
TEST_SNIPPET = r"""
def get_overall_snippet_messages_dets(snippet, blocks_dets):
'''
Returns messages which apply to snippet as a whole, not just specific
blocks. E.g. looking at every block to look for opportunities to unpack. Or
reporting on linting results.
'''
messages_dets = []
p1 = print
p2 = 'print'
print = 'sausage'
p3 = print
"""
PY3_6 = '3.6'
......@@ -104,7 +101,7 @@ METHOD_LBL = 'method'
EMAIL2USE = 'superhelp@p-s.co.nz'
INTRO = ("Help is provided for your overall snippet and for each block of code "
"as appropriate.")
"as appropriate. If there is nothing to say about a block it is skipped.")
NO_ADVICE_MESSAGE = ("No advice to give - looks fine :-). But if you think "
f"there should have been some advice given, contact {EMAIL2USE} "
"with the subject line 'Advice' and explain. Please include a snippet to "
......
......@@ -44,6 +44,7 @@ def _get_snippet(snippet, file_path):
else:
snippet = conf.TEST_SNIPPET
logging.info("Using default snippet because no snippet provided")
snippet = snippet.strip('\n')
return snippet
def _get_displayer_module(displayer):
......
This diff is collapsed.
from textwrap import dedent
from tests import check_as_expected
ROOT = 'superhelp.advisors.print_advisors.'
def test_misc():
test_conf = [
(
dedent("""\
pet = 'cat'
"""),
{
ROOT + 'print_overview': 0,
}
),
(
dedent("""\
pet = 'cat'
print(pet)
"""),
{
ROOT + 'print_overview': 1,
}
),
(
dedent("""\
for i in range(2):
print('Hi')
"""),
{
ROOT + 'print_overview': 1,
}
),
(
dedent("""\
for i in range(2):
print(f'Hi {i}')
"""),
{
ROOT + 'print_overview': 1,
}
),
(
dedent("""\
print('Hi')
print('Hi')
print('Hi')
"""),
{
ROOT + 'print_overview': 1,
}
),
(
dedent("""\
p = print
p('Hi')
p('Hi')
"""),
{
ROOT + 'print_overview': 1,
}
),
]
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