Skip to content

Commit

Permalink
Merge pull request #78 from gsarma/dev
Browse files Browse the repository at this point in the history
Allow specifying location of generated files.
  • Loading branch information
rgerkin committed Jun 2, 2018
2 parents 71148a9 + 83786e0 commit 7cbb6c9
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 27 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ docs/chapter*.py
.coverage
htmlcov
Untitled*.ipynb
GeneratedFiles
GeneratedFiles/*.py
98 changes: 71 additions & 27 deletions sciunit/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,25 @@
'KERNEL':('ipykernel' in sys.modules),
'CWD':os.path.realpath(sciunit.__path__[0])}

def rec_apply(func, n):
"""
Used to determine parent directory n levels up
by repeatedly applying os.path.dirname
"""
if n > 1:
rec_func = rec_apply(func, n - 1)
return lambda x: func(rec_func(x))
return func

def printd_set(state):
"""Enable the printd function.
"""Enable the printd function.
Call with True for all subsequent printd commands to be passed to print.
Call with False to ignore all subsequent printd commands.
Call with False to ignore all subsequent printd commands.
"""

global settings
settings['PRINT_DEBUG_STATE'] = (state is True)


def printd(*args, **kwargs):
"""Print if PRINT_DEBUG_STATE is True"""

Expand Down Expand Up @@ -77,11 +86,27 @@ def assert_dimensionless(value):
class NotebookTools(object):
"""A class for manipulating and executing Jupyter notebooks."""

path = '' # Relative path to the parent directory of the notebook.
gen_dir_name = 'GeneratedFiles' # Name of directory where files generated by do_notebook are stored
gen_file_level = 2 # Number of levels up from notebook directory where generated files are stored

def __init__(self, *args, **kwargs):
super(NotebookTools,self).__init__(*args, **kwargs)
self.fix_display()

path = '' # Relative path to the parent directory of the notebook.
@classmethod
def convert_path(cls, file):
"""
Check to see if an extended path is given and convert appropriately
"""

if isinstance(file,str):
return file
elif isinstance(file, list) and all([isinstance(x, str) for x in file]):
return "/".join(file)
else:
print("Incorrect path specified")
return -1

def get_path(self, file):
"""Get the full path of the notebook found in the directory
Expand Down Expand Up @@ -118,7 +143,7 @@ def load_notebook(self, name):

def run_notebook(self, nb, f):
"""Runs a loaded notebook file."""

if PYTHON_MAJOR_VERSION == 3:
kernel_name = 'python3'
elif PYTHON_MAJOR_VERSION == 2:
Expand Down Expand Up @@ -150,7 +175,8 @@ def convert_notebook(self, name):
#subprocess.call(["jupyter","nbconvert","--to","python",
# self.get_path("%s.ipynb"%name)])
exporter = nbconvert.exporters.python.PythonExporter()
file_path = self.get_path("%s.ipynb"%name)
relative_path = self.convert_path(name)
file_path = self.get_path("%s.ipynb"%relative_path)
code = exporter.from_filename(file_path)[0]
self.write_code(name, code)
self.clean_code(name, [])#'get_ipython'])
Expand All @@ -162,24 +188,43 @@ def convert_and_execute_notebook(self, name):
code = self.read_code(name)#clean_code(name,'get_ipython')
exec(code,globals())

def gen_file_path(self, name):
"""
Returns full path to generated files. Checks to see if directory
exists where generated files are stored and creates one otherwise.
"""
relative_path = self.convert_path(name)
file_path = self.get_path("%s.ipynb"%relative_path)
parent_path = rec_apply(os.path.dirname, self.gen_file_level)(file_path)
gen_file_name = name if isinstance(name,str) else name[1] #Name of generated file
gen_dir_path = self.get_path(os.path.join(parent_path, self.gen_dir_name))
if not os.path.exists(gen_dir_path): # Create folder for generated files if needed
os.makedirs(gen_dir_path)
new_file_path = self.get_path('%s.py'%os.path.join(gen_dir_path, gen_file_name))
return new_file_path

def read_code(self, name):
"""Reads code from a python file called 'name'"""

file_path = self.get_path('%s.py'%name)
file_path = self.gen_file_path(name)
with open(file_path) as f:
code = f.read()
return code

def write_code(self, name, code):
"""Writes code to a python file called 'name',
erasing the previous contents."""

file_path = self.get_path('%s.py'%name)
"""
Writes code to a python file called 'name',
erasing the previous contents.
Files are created in a directory specified by gen_dir_name (see function gen_file_path)
File name is second argument of path
"""
file_path = self.gen_file_path(name)
with open(file_path,'w') as f:
f.write(code)

def clean_code(self, name, forbidden):
"""Remove lines containing items in 'forbidden' from the code.
"""
Remove lines containing items in 'forbidden' from the code.
Helpful for executing converted notebooks that still retain IPython
magic commands.
"""
Expand Down Expand Up @@ -208,7 +253,7 @@ def strip_line_magic(cls, line, magics_allowed):
if line == stripped:
printd("No line magic pattern match in '%s'" % line)
if magic_kind and magic_kind not in magics_allowed:
stripped = "" # If the part after the magic won't work,
stripped = "" # If the part after the magic won't work,
# just get rid of it
return stripped

Expand Down Expand Up @@ -250,7 +295,6 @@ def strip_line_magic_v2(cls,line):
def do_notebook(self, name):
"""Runs a notebook file after optionally
converting it to a python file."""

CONVERT_NOTEBOOKS = int(os.getenv('CONVERT_NOTEBOOKS',True))
s = StringIO()
if mock:
Expand Down Expand Up @@ -304,7 +348,7 @@ def import_all_modules(package, skip=None, verbose=False, prefix="", depth=0):
module = '%s.%s' % (package.__name__,modname)
subpackage = importlib.import_module(module)
if ispkg:
import_all_modules(subpackage, skip=skip,
import_all_modules(subpackage, skip=skip,
verbose=verbose,depth=depth+1)


Expand All @@ -323,29 +367,29 @@ def import_module_from_path(module_path, name=None):
from importlib import import_module
module_name = file_name.rstrip('.py')
module = import_module(module_name)
sys.path.pop() # Remove the directory that was just added.
sys.path.pop() # Remove the directory that was just added.
return module

def dict_hash(d):
return SciUnit.dict_hash(d)


def method_cache(by='value',method='run'):
"""A decorator used on any model method which calls the model's 'method'
method if that latter method has not been called using the current
arguments or simply sets model attributes to match the run results if
it has."""
"""A decorator used on any model method which calls the model's 'method'
method if that latter method has not been called using the current
arguments or simply sets model attributes to match the run results if
it has."""

def decorate_(func):
def decorate(*args, **kwargs):
model = args[0] # Assumed to be self.
model = args[0] # Assumed to be self.
assert hasattr(model,method), "Model must have a '%s' method."%method
if func.__name__ == method: # Run itself.
if func.__name__ == method: # Run itself.
method_args = kwargs
else: # Any other method.
else: # Any other method.
method_args = kwargs[method] if method in kwargs else {}
if not hasattr(model.__class__,'cached_runs'): # If there is no run cache.
model.__class__.cached_runs = {} # Create the method cache.
if not hasattr(model.__class__,'cached_runs'): # If there is no run cache.
model.__class__.cached_runs = {} # Create the method cache.
cache = model.__class__.cached_runs
if by == 'value':
model_dict = {key:value for key,value in list(model.__dict__.items()) \
Expand Down Expand Up @@ -423,7 +467,7 @@ def config_get(key, default=None):
try:
assert isinstance(key,str), "Config key must be a string"
config_path = os.path.join(settings['CWD'],'config.json')
value = config_get_from_path(config_path,key)
value = config_get_from_path(config_path,key)
except Exception as e:
if default is not None:
log(e)
Expand Down

0 comments on commit 7cbb6c9

Please sign in to comment.