Skip to content

Commit

Permalink
Add SASsession magic to reduce prompting (#79)
Browse files Browse the repository at this point in the history
* add sas2nb for binder and SO question

* bump version 2.4.4 after merge

* add iris.sas test file

* change github workflow to not upload wheel.

* workaround binder wheel install issue.

* fix data packaging for pypi

* bump version

* fix grammar in message.

* clean up binder postBuild

* add SASsession magic to reduce prompting

* add lst_len property to fix _which_display

* Fix typing issue

* bump version
  • Loading branch information
jld23 authored May 21, 2021
1 parent c187ae4 commit 83678c2
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 31 deletions.
4 changes: 0 additions & 4 deletions binder/postBuild
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@
# limitations under the License.
#

# pip install --force-reinstall -i https://test.pypi.org/pypi/ --extra-index-url https://pypi.org/simple SAS-kernel==2.4.4.dev8

pip install --force-reinstall --no-deps . --no-binary :all:

# copy sascfg_personal.py to default location
mkdir -p ~/.config/saspy/
cp ./binder/sascfg_personal.py ~/.config/saspy/
Expand Down
38 changes: 14 additions & 24 deletions sas_kernel/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import logging
import saspy

from typing import Tuple
from typing import Tuple, Union
from IPython.display import HTML
from metakernel import MetaKernel
from .version import __version__
Expand Down Expand Up @@ -64,9 +64,9 @@ def __init__(self, **kwargs):
self.strproclist = '\n'.join(str(x) for x in self.proclist)
self.promptDict = {}
MetaKernel.__init__(self, **kwargs)
self.lst_len = 0
self.mva = None
self.cachedlog = None
self.lst_len = -99 # initialize the length to a negative number to trigger function
self._allow_stdin = False

def do_apply(self, content, bufs, msg_id, reply_metadata):
Expand All @@ -87,18 +87,13 @@ def _get_config_names(self):
loader.exec_module(cfg)
return cfg.SAS_config_names

def _get_lst_len(self):
code = "data _null_; run;"
res = self.mva.submit(code)
assert isinstance(res, dict)
self.lst_len = len(res['LST'])
assert isinstance(self.lst_len, int)
return

def _start_sas(self):
def _start_sas(self, **kwargs):
session_params = kwargs
if session_params is not None:
for _, v in session_params.items():
assert isinstance(v, str)
try:
# import saspy as saspy
self.mva = saspy.SASsession(kernel=self)
self.mva = saspy.SASsession(kernel=self, **kwargs)
except KeyError:
self.mva = None
except OSError:#socket.gaierror
Expand All @@ -111,9 +106,10 @@ def _start_sas(self):
""".format(saspy.list_configs()[0], ', '.join(self._get_config_names()))
self.Error_display(msg)
self.mva = None
except:
print("Unexpected error:", sys.exc_info()[0])
raise
except Exception:
# self.Error_display('\n'.join(list(sys.exc_info())))
self.Error_display(str([l for l in sys.exc_info()]))
# return


def _colorize_log(self, log: str) -> str:
Expand Down Expand Up @@ -169,10 +165,8 @@ def _which_display(self, log: str, output: str = '') -> str:
"""
error_count, msg_list, error_line_list = self._is_error_log(log)

# store the log for display in the showSASLog nbextension
#self.cachedlog = self._colorize_log(log)

# no error and LST output
print(error_count, len(output))
if error_count == 0 and len(output) > self.lst_len:
return self.Display(HTML(output))

Expand All @@ -193,7 +187,7 @@ def _which_display(self, log: str, output: str = '') -> str:
# for everything else return the log
return self.Print(self._colorize_log(log))

def do_execute_direct(self, code: str, silent: bool = False) -> [str, dict]:
def do_execute_direct(self, code: str, silent: bool = False) -> Union[str, dict]:
"""
This is the main method that takes code from the Jupyter cell
and submits it to the SAS server.
Expand All @@ -211,10 +205,6 @@ def do_execute_direct(self, code: str, silent: bool = False) -> [str, dict]:
self._allow_stdin = True
self._start_sas()

# This code is now handeled in saspy will remove in future version
if self.lst_len < 0:
self._get_lst_len()

# This block uses special strings submitted by the Jupyter notebook extensions
if not code.startswith('showSASLog_11092015') and \
not code.startswith("CompleteshowSASLog_11092015"):
Expand Down
4 changes: 2 additions & 2 deletions sas_kernel/magics/log_magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def line_showFullLog(self):
if self.kernel.mva is None:
self.kernel._allow_stdin = True
self.kernel._start_sas()
print("Session Started probably not the log you want")
print("Session Started. Probably not the log you want.")
return self.kernel._which_display(self.kernel.mva.saslog())

def register_magics(kernel):
Expand All @@ -60,4 +60,4 @@ def showLog(line):

@register_line_magic
def showFullLog(line):
kernel.call_magic("%showFullLog " + line)
kernel.call_magic("%showFullLog " + line)
53 changes: 53 additions & 0 deletions sas_kernel/magics/sas_session_magic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#
# Copyright SAS Institute
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from metakernel import Magic

class SASsessionMagic(Magic):
def __init__(self, *args, **kwargs):
super(SASsessionMagic, self).__init__(*args, **kwargs)

def line_SASsession(self, *args):
"""
SAS Kernel magic allows a programatic way to submit configuration
details.
This magic is only available within the SAS Kernel
"""
if len(args) > 1:
args = ''.join(args)
elif len(args) == 1:
args = ''.join(args[0])
args = args.replace(' ', '')
args = args.replace('"', '')
args = args.replace("'", '')
sess_params = dict(s.split('=') for s in args.split(','))
self.kernel._allow_stdin = True
self.kernel._start_sas(**sess_params)

def register_magics(kernel):
kernel.register_magics(SASsessionMagic)


def register_ipython_magics():
from metakernel import IPythonKernel
from IPython.core.magic import register_line_magic
kernel = IPythonKernel()
magic = SASsessionMagic(kernel)
# Make magics callable:
kernel.line_magics["SASsession"] = magic

@register_line_magic
def SASsession(line):
kernel.call_magic("%SASsession " + line)
2 changes: 1 addition & 1 deletion sas_kernel/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
__version__ = '2.4.9'
__version__ = '2.4.10'

0 comments on commit 83678c2

Please sign in to comment.