Commit 9bfb8f8c authored by Grant Paton-Simpson's avatar Grant Paton-Simpson

Fix major bug with file permissions; misc

* Use temp files as needed to fix bug with file permissions
* Add XKCD easter egg
* Add "import community" easter egg
* Handle need to run some modules as scripts as well as like library modules
* Bump version
parent c6ee2a48
Pipeline #569 canceled with stages
# https://git.nzoss.org.nz/pyGrant/superhelp
version number: 0.9.7
version number: 0.9.8
author: Grant Paton-Simpson
## Overview
......
......@@ -2,7 +2,7 @@ from setuptools import setup, find_packages # @UnresolvedImport
from codecs import open
from os import path
__version__ = '0.9.7'
__version__ = '0.9.8'
here = path.abspath(path.dirname(__file__))
......
......@@ -140,9 +140,10 @@ def get_advisor_comments():
all_advisors_dets = (
FILT_BLOCK_ADVISORS + ANY_BLOCK_ADVISORS + SNIPPET_ADVISORS)
for advisor_dets in all_advisors_dets:
source = advisor_dets.advisor.__module__.split('.')[-1]
docstring = advisor_dets.advisor.__doc__
advisor_comment = get_docstring_start(docstring)
advisor_comments.append(advisor_comment)
advisor_comments.append((advisor_comment, source))
return advisor_comments
## =============================================================================
......
import datetime
import logging
from pathlib import Path
t = True
f = False
......@@ -27,10 +26,9 @@ else:
## When testing user-supplied snippets watch out for the BOM MS inserts via Notepad. AST chokes on it.
TEST_SNIPPET = """\
#import re
from re import match
#flag = re.VERBOSE
#t_OPERATOR = r'(?x) <> | <= | >= | = | < | >'
pets = 'seagull'
home = 'super volcano'
appliance = 'garbage disposal'
"""
DEMO_SNIPPET = """\
......@@ -161,7 +159,7 @@ PY3_6 = '3.6'
PY3_7 = '3.7'
PY3_8 = '3.8'
AST_OUTPUT_XML = Path(__file__).parent / 'ast_output.xml'
AST_OUTPUT_XML_FNAME = 'ast_output.xml'
PYTHON_CODE_START = '__python_code_start__'
PYTHON_CODE_END = '__python_code_end__'
......@@ -230,6 +228,8 @@ MISSING_ADVICE_MESSAGE = ("If there was some advice you think should have "
"'Advice' and explain. Please include a snippet to test as well.")
SYSTEM_MESSAGE = 'System message'
XKCD_WARNING_WORDS = ['supervolcano', 'seagull', 'garbage disposal']
VERBOSE_FLAG = 'VERBOSE'
INLINE_RE_VERBOSE_FLAG = '(?x)'
......
......@@ -3,6 +3,7 @@ from textwrap import dedent, indent
import webbrowser
from .. import conf
from ..utils import make_tmp_file
from markdown import markdown ## https://coderbook.com/@marcus/how-to-render-markdown-syntax-as-html-using-python/ @UnresolvedImport
......@@ -677,8 +678,7 @@ def display(snippet, messages_dets, *,
missing_advice_message=conf.MISSING_ADVICE_MESSAGE,
body_inner=body_inner,
visibility_script=VISIBILITY_SCRIPT)
explained_fpath = Path.cwd() / 'explained.html'
with open(explained_fpath, 'w') as f:
f.write(html2write)
url = explained_fpath.as_uri()
tmp_fh, fpath = make_tmp_file('superhelp_output.html', mode='w')
tmp_fh.write(html2write)
url = fpath.as_uri()
webbrowser.open_new_tab(url)
import argparse
import logging
from superhelp import conf
try:
from .. import conf # @UnresolvedImport @UnusedImport
## 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 . import advisors, messages # @UnusedImport
from .displayers import cli_displayer, html_displayer # @UnusedImport
except (ImportError, ValueError):
from pathlib import Path
import sys
parent = Path.cwd().parent
sys.path.insert(0, parent)
from superhelp import conf, advisors, messages # @Reimport
from superhelp.displayers import cli_displayer, html_displayer # @Reimport
logging.basicConfig(
level=conf.LOG_LEVEL,
format='%(asctime)s %(levelname)-8s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
## 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, messages
from superhelp.displayers import cli_displayer, html_displayer
advisors.load_advisors()
do_test = conf.DO_TEST ## use test snippet rather than the larger demo snippet
......@@ -67,11 +74,18 @@ def get_advice(snippet=None, *, file_path=None, displayer='html',
not sent to browser but returned for display by notebook itself
"""
snippet = _get_snippet(snippet, file_path)
try:
messages_dets, multi_block = messages.get_snippet_dets(snippet)
except Exception as e:
messages_dets = messages.get_error_messages_dets(e, snippet)
if snippet.strip() == 'import community':
messages_dets = messages.get_community_message(snippet)
multi_block = False
elif all([word in snippet for word in conf.XKCD_WARNING_WORDS]):
messages_dets = messages.get_xkcd_warning(snippet)
multi_block = False
else:
try:
messages_dets, multi_block = messages.get_snippet_dets(snippet)
except Exception as e:
messages_dets = messages.get_error_messages_dets(e, snippet)
multi_block = False
displayer_module = _get_displayer_module(displayer)
if displayer_module:
res = display_messages(displayer_module, snippet, messages_dets,
......@@ -79,19 +93,6 @@ def get_advice(snippet=None, *, file_path=None, displayer='html',
multi_block=multi_block)
if in_notebook:
return res
#TODO: allow extra in help
# for comment in advisors.get_advisor_comments():
# print(comment)
def shelp():
"""
......@@ -115,7 +116,17 @@ def shelp():
parser.add_argument('-d', '--displayer', type=str,
required=False, default=default_displayer,
help="Where do you want your help shown? html, cli, etc")
parser.add_argument('-a', '--advice-list', action='store_true',
default=False,
help="List available advice")
args = parser.parse_args()
if args.advice_list:
advisor_comments = advisors.get_advisor_comments()
num_width = len(str(len(advisor_comments)))
for n, (comment, source) in enumerate(
advisor_comments, 1):
print(f"{n:>{num_width}}) {comment} ({source})")
return
displayer = args.displayer if do_displayer else None
get_advice(args.snippet,
file_path=args.file_path, displayer=displayer, message_level=args.level)
......
......@@ -83,8 +83,17 @@ 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 get_docstring_start, layout_comment # @UnresolvedImport
try:
from . import advisors, ast_funcs, conf # @UnresolvedImport @UnusedImport
from ..utils import get_docstring_start, layout_comment as layout, make_tmp_file # @UnresolvedImport @UnusedImport
except (ImportError, ValueError):
from pathlib import Path
import sys
parent = Path.cwd().parent
sys.path.insert(0, parent)
from superhelp import advisors, ast_funcs, conf # @Reimport
from superhelp.utils import get_docstring_start, layout_comment as layout, make_tmp_file # @Reimport
BlockDets = namedtuple(
'BlockDets', 'element, pre_block_code_str, block_code_str, first_line_no')
......@@ -95,6 +104,7 @@ MessageDets = namedtuple('MessageDets',
'code_str, message, first_line_no, warning, source')
MessageDets.__doc__ += (
"All the bits and pieces that might be needed to craft a message")
MessageDets.code_str.__doc__ = ("The block of code the message relates to")
MessageDets.source.__doc__ = ("A unique identifier of the source of message "
"- useful for auditing / testing")
......@@ -173,15 +183,16 @@ def get_message_dets_from_input(advisor_dets, *,
except Exception as e:
message = {
conf.BRIEF: (
layout_comment(f"""\
layout(f"""\
### Advisor "`{name}`" unable to run
Advisor {name} unable to run. Advisor description:
""")
+ ## show first line of docstring (subsequent lines might have more technical, internally-oriented comments)
layout_comment(get_docstring_start(docstring) + '\n')
layout(get_docstring_start(docstring) + '\n')
+
layout_comment(str(e))
layout(str(e))
)
}
source = conf.SYSTEM_MESSAGE
......@@ -310,7 +321,9 @@ def get_separated_messages_dets(snippet, snippet_block_els, xml):
return overall_snippet_messages_dets, block_level_messages_dets
def store_ast_output(xml):
xml.getroottree().write(str(conf.AST_OUTPUT_XML), pretty_print=True)
_tmp_ast_fh, tmp_ast_output_xml_fpath = make_tmp_file(
conf.AST_OUTPUT_XML_FNAME, mode='w')
xml.getroottree().write(str(tmp_ast_output_xml_fpath), pretty_print=True)
logging.info("\n\n\n\n\nUpdating AST\n\n\n\n\n")
def get_snippet_dets(snippet):
......@@ -324,25 +337,55 @@ def get_snippet_dets(snippet):
snippet, snippet_block_els, xml)
return snippet_messages_dets, multi_block_snippet
def get_error_messages_dets(e, snippet):
def get_system_messages_dets(snippet, brief_message, *, warning=True):
"""
If unable to produce any messages, supply the problem in the form of
standard messages_dets so the displayers can operate in their usual
Even though only one message is needed, supplying the details in the
standard format the displayers expect means they can operate in their usual
messages_dets consuming ways :-).
"""
message = {
conf.BRIEF: layout_comment(f"""\
### No advice sorry :-(
Unable to provide advice - some sort of problem.
Details: {e}
"""),
conf.BRIEF: brief_message,
}
message = complete_message(message, source=conf.SYSTEM_MESSAGE)
overall_messages_dets = [
MessageDets(snippet, message,
first_line_no=None, warning=True, source=conf.SYSTEM_MESSAGE)]
first_line_no=None, warning=warning, source=conf.SYSTEM_MESSAGE)]
block_messages_dets = []
messages_dets = (overall_messages_dets, block_messages_dets)
return messages_dets
def get_error_messages_dets(e, snippet):
"""
If unable to produce any messages, supply the problem in the form of
standard messages_dets so the displayers can operate in their usual
messages_dets consuming ways :-).
"""
brief_message = layout(f"""\
### No advice sorry :-(
Unable to provide advice - some sort of problem.
Details: {e}
""")
return get_system_messages_dets(snippet, brief_message)
def get_community_message(snippet):
brief_message = layout("""\
### Join in!
Python has always had a great community. Learn more at
<https://www.python.org/community/>. Better still - get involved :-)
""")
return get_system_messages_dets(snippet, brief_message)
def get_xkcd_warning(snippet):
brief_message = layout("""\
### According to XKCD this code could be *very* dangerous
See <https://xkcd.com/2261/>
""")
return get_system_messages_dets(snippet, brief_message)
import logging
from os import rename
from pathlib import Path
import sys
import tempfile
from textwrap import dedent, wrap
from . import conf
def make_tmp_file(fname, mode='w'):
tmp_fh = tempfile.NamedTemporaryFile(mode=mode, delete=False)
randomly_named_fpath = Path(tmp_fh.name)
fpath = Path(randomly_named_fpath.parent) / fname
rename(randomly_named_fpath, fpath)
return tmp_fh, fpath
def get_python_version():
major, minor = sys.version_info[:2]
return f"{major}.{minor}"
......
## cd ~/projects/superhelp && superhelp/env/bin/python3 -m nose
import astpath
from nose.tools import assert_equal, assert_not_equal, assert_true, assert_false # @UnusedImport @UnresolvedImport
from superhelp import conf
from superhelp.messages import _get_tree, get_separated_messages_dets, \
store_ast_output
try:
from ..superhelp import conf # @UnresolvedImport
from ..superhelp.messages import _get_tree, get_separated_messages_dets, store_ast_output # @UnresolvedImport
except (ImportError, ValueError):
from pathlib import Path
import sys
parent = Path.cwd().parent
sys.path.insert(0, parent)
from superhelp import conf # @Reimport
from superhelp.messages import _get_tree, get_separated_messages_dets, store_ast_output # @Reimport
def get_actual_source_freqs(messages_dets, expected_source_freqs):
"""
......
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