diff --git a/pynest/nest/lib/hl_api_helper.py b/pynest/nest/lib/hl_api_helper.py index f10f577ea9..4ad8a798ba 100644 --- a/pynest/nest/lib/hl_api_helper.py +++ b/pynest/nest/lib/hl_api_helper.py @@ -26,8 +26,14 @@ import warnings import inspect +import json import functools import textwrap +import subprocess +import os +import re + +from string import Template # These variables MUST be set by __init__.py right after importing. # There is no safety net, whatsoever. @@ -111,7 +117,6 @@ def deprecated(alt_func_name, text=None): function: Decorator function """ - def deprecated_decorator(func): _deprecation_warning[func.__name__] = True @@ -172,6 +177,7 @@ def is_string(obj): """ return isinstance(obj, uni_str) + __debug = False @@ -229,11 +235,11 @@ def stack_checker_func(*args, **kwargs): if not get_debug(): return f(*args, **kwargs) else: - sr('count') - stackload_before = spp() + sr + stackload_before = spp result = f(*args, **kwargs) - sr('count') - num_leftover_elements = spp() - stackload_before + sr + num_leftover_elements = spp - stackload_before if num_leftover_elements != 0: eargs = (f.__name__, num_leftover_elements) etext = "Function '%s' left %i elements on the stack." @@ -389,17 +395,146 @@ def broadcast(item, length, allowed_types, name="item"): """ if isinstance(item, allowed_types): - return length * (item, ) + return length * (item,) elif len(item) == 1: return length * item elif len(item) != length: - raise TypeError("'%s' must be a single value, a list with " + - "one element or a list with %i elements." - % (name, length)) - + raise TypeError("'{0}' must be a single value, a list with " + + "one element or a list with {1} elements.".format( + name, length)) return item +def __check_nb(): + """Return true if called from a Jupyter notebook.""" + try: + return get_ipython().__class__.__name__.startswith('ZMQ') + except NameError: + return False + + +def __show_help_in_modal_window(objname, hlptxt): + """Open modal window with help text + + Parameters + ---------- + objname : str + filename + hlptxt : str + Full text + """ + + hlptxt = json.dumps(hlptxt) + style = "" + s = Template(""" + require( + ["base/js/dialog"], + function(dialog) { + dialog.modal({ + title: '$jstitle', + body: $jstext, + buttons: { + 'close': {} + } + }); + } + ); + """) + + from IPython.display import HTML, Javascript, display + display(HTML(style)) + + display(Javascript(s.substitute(jstitle=objname, jstext=hlptxt))) + + +def show_help_with_pager(hlpobj, pager): + """Output of doc in python with pager or print + + Parameters + ---------- + hlpobj : object + Object to display + pager: str, optional + pager to use, NO if you explicity do not want to use a pager + """ + if 'NEST_INSTALL_DIR' not in os.environ: + print( + 'NEST help needs to know where NEST is installed.' + 'Please source nest_vars.sh or define NEST_INSTALL_DIR manually.') + return + + helpdir = os.path.join(os.environ['NEST_INSTALL_DIR'], "share", "doc", + "nest", "help") + objname = hlpobj + '.hlp' + consolepager = ['less', 'more', 'vi', 'vim', 'nano', 'emacs -nw', + 'ed', 'editor'] + + # reading ~/.nestrc lookink for pager to use. + if pager is None: + # check if .netsrc exist + rc_file = os.path.join(os.environ['HOME'], '.nestrc') + if os.path.isfile(rc_file): + # open ~/.nestrc + rc = open(rc_file, 'r') + # The loop goes through the .nestrc line by line and checks + # it for the definition of a pager. Whether a pager is + # found or not, this pager is used or the standard pager 'more'. + for line in rc: + # the re checks if there are lines beginning with '%' + rctst = re.match(r'^\s?%', line) + if rctst is None: + # the next re checks for a sth. like + # '/page << /command (more)' + # and returns the given pager. + pypagers = re.findall( + r'^\s?/page\s?<<\s?/command\s?\((\w*)', line) + if pypagers: + for pa in pypagers: + if pa: + pager = pa + else: + pager = 'more' + break + else: + pager = 'more' + rc.close() + else: + pager = 'more' + hlperror = True + # Searching the given object in all helpfiles, check the environment + # and display the helptext in the pager. + for dirpath, dirnames, files in os.walk(helpdir): + for hlp in files: + if hlp == objname: + hlperror = False + objf = os.path.join(dirpath, objname) + fhlp = open(objf, 'r') + hlptxt = fhlp.read() + # only for notebook + if __check_nb(): + if pager in consolepager: + # only in notebook open modal window + __show_help_in_modal_window(objname, hlptxt) + fhlp.close() + break + else: + subprocess.call([pager, objf]) + fhlp.close() + break + else: + if pager in consolepager: + subprocess.call([pager, objf]) + fhlp.close() + break + else: + subprocess.call([pager, objf]) + fhlp.close() + break + if hlperror: + print("Sorry, there is no help for '" + hlpobj + "'!") + + @check_stack def get_verbosity(): """Return verbosity level of NEST's messages. @@ -412,8 +547,8 @@ def get_verbosity(): # Defined in hl_api_helper to avoid circular inclusion problem with # hl_api_info.py - sr('verbosity') - return spp() + sr + return spp @check_stack @@ -429,7 +564,7 @@ def set_verbosity(level): # Defined in hl_api_helper to avoid circular inclusion problem with # hl_api_info.py - sr("%s setverbosity" % level) + sr def model_deprecation_warning(model): diff --git a/pynest/nest/lib/hl_api_info.py b/pynest/nest/lib/hl_api_info.py index e78b43ce6f..9d3da1db75 100644 --- a/pynest/nest/lib/hl_api_info.py +++ b/pynest/nest/lib/hl_api_info.py @@ -24,6 +24,9 @@ """ from .hl_api_helper import * +import sys +import os +import webbrowser @check_stack @@ -55,26 +58,38 @@ def authors(): @check_stack -def helpdesk(browser="firefox"): - """Open the NEST helpdesk in the given browser. +def helpdesk(): + """Open the NEST helpdesk in browser. - The default browser is firefox. - - Parameters - ---------- - browser : str, optional - Name of the browser to use + Use the system default browser. """ + if 'NEST_DOC_DIR' not in os.environ: + print( + 'NEST help needs to know where NEST is installed.' + 'Please source nest_vars.sh or define NEST_DOC_DIR manually.') + return - sr("/helpdesk << /command (%s) >> SetOptions" % browser) - sr("helpdesk") + helpfile = os.path.join(os.environ['NEST_DOC_DIR'], 'help', + 'helpindex.html') + + # Under Windows systems webbrowser.open is incomplete + # See + if sys.platform[:3] == "win": + os.startfile(helpfile) + + # Under MacOs we need to ask for the browser explicitly. + # See . + if sys.platform[:3] == "dar": + webbrowser.get('safari').open_new(helpfile) + else: + webbrowser.open_new(helpfile) @check_stack -def help(obj=None, pager="less"): +def help(obj=None, pager=None): """Show the help page for the given object using the given pager. - The default pager is less. + The default pager is more. Parameters ---------- @@ -83,17 +98,17 @@ def help(obj=None, pager="less"): pager : str, optional Pager to use """ + hlpobj = obj + if hlpobj is not None: + show_help_with_pager(hlpobj, pager) - if obj is not None: - sr("/page << /command (%s) >> SetOptions" % pager) - sr("/%s help" % obj) else: print("Type 'nest.helpdesk()' to access the online documentation " "in a browser.") print("Type 'nest.help(object)' to get help on a NEST object or " "command.\n") print("Type 'nest.Models()' to see a list of available models " - "in NEST.\n") + "in NEST.") print("Type 'nest.authors()' for information about the makers " "of NEST.") print("Type 'nest.sysinfo()' to see details on the system " @@ -115,6 +130,7 @@ def get_argv(): tuple: Argv, as seen by NEST. """ + sr('statusdict') statusdict = spp() return statusdict['argv']