def test_should_set_docstring_conf_path_relatively_to_top_directory(work_dir): """ Test load valid conf file """ conf_file = join(work_dir, '.mlvtools') write_conf(work_dir=work_dir, conf_path=conf_file, docstring_conf='./doc_conf.yml') conf = load_conf_or_default(conf_file, working_directory=work_dir) assert conf.docstring_conf == join(work_dir, './doc_conf.yml')
def test_should_detect_consistency_for_notebooks(work_dir): """ Test check_all_scripts_consistency exit without error when there is consistency between all notebooks and scripts from the provided directory """ notebook_dir = join(CURRENT_DIR, 'data', 'notebooks') script_dir = join(CURRENT_DIR, 'data', 'script_dir') write_conf(work_dir, conf_path=join(work_dir, DEFAULT_CONF_FILENAME), script_dir=script_dir, dvc_cmd_dir=work_dir) check_call(['check_all_scripts_consistency', '-n', notebook_dir, '-w', work_dir])
def setup_with_conf(work_dir: str, conf_path: str = None, docstring_conf_path: str = None) -> Tuple[str, str]: write_conf(work_dir=work_dir, conf_path=conf_path, ignore_keys=['# Ignore'], dvc_cmd_dir='./dvc_cmd', docstring_conf=docstring_conf_path) script_path = join(work_dir, 'script_path.py') docstring = '"""\n:param out:\n:dvc-out out: {{ conf.out_file }}\n"""' if docstring_conf_path else None write_min_script(script_path, docstring) return conf_path, script_path
def test_should_detect_inconsistency_if_at_least_one_inconsistent_script(work_dir): """ Test check_all_scripts_consistency exit with error if there is at least one inconsistency """ notebook_dir, script_dir = temporary_setup(work_dir) # Replace a script content with open(join(script_dir, listdir(script_dir)[0]), 'w') as fd: fd.write('print("Hello world!")') write_conf(work_dir, conf_path=join(work_dir, DEFAULT_CONF_FILENAME), script_dir=script_dir, dvc_cmd_dir=work_dir) ret_code = call(['check_all_scripts_consistency', '-n', notebook_dir, '-w', work_dir]) assert ret_code != 0
def test_should_generate_python_script_with_conf_auto_detect(work_dir, mocker): """ Convert a Jupyter Notebook to a Python 3 script using conf """ mocked_check_output = mocker.patch('subprocess.check_output', return_value=work_dir.encode()) kept_cells, dropped_cells, docstring, notebook_path = generate_test_notebook( work_dir=work_dir, notebook_name='test_nb.ipynb') # Create conf with knew ignore cell keywords conf_data = write_conf(work_dir=work_dir, conf_path=join(work_dir, DEFAULT_CONF_FILENAME), ignore_keys=['# Ignore', 'import']) cmd_arguments = ['-n', notebook_path] IPynbToPython().run(*cmd_arguments) # This path is generated using the conf script_dir and the notebook name output_script_path = join(work_dir, conf_data['path']['python_script_root_dir'], 'mlvtools_test_nb.py') assert exists(output_script_path) with open(output_script_path, 'r') as fd: file_content = fd.read() # With those knew keywords from conf the first code cell must be ignored (due to import) # The second no effect cell must remain because "# No effect" is no more a keyword assert not is_in(kept_cells.code[0][1], file_content) assert is_in(dropped_cells.no_effect[1][1], file_content) # Check conf file has been found using git rev-parse command assert mocked_check_output.mock_calls == [ mocker.call(['git', 'rev-parse', '--show-toplevel'], cwd=work_dir) ]
def test_should_handle_notebook_with_invalid_python_name_with_conf( work_dir, mocker): """ Test invalid python filename are converted """ notebook_path = gen_notebook(cells=[('code', 'pass')], tmp_dir=work_dir, file_name='01_(test) nb.ipynb') # Create conf in working directory conf_data = write_conf(work_dir=work_dir, conf_path=join(work_dir, DEFAULT_CONF_FILENAME), ignore_keys=['# Ignore', 'remove=']) cmd_arguments = ['-n', notebook_path, '-w', work_dir] IPynbToPython().run(*cmd_arguments) # This path is generated using the conf script_dir and the notebook name output_script_path = join(work_dir, conf_data['path']['python_script_root_dir'], 'mlvtools_01__test_nb.py') assert exists(output_script_path) with open(output_script_path, 'r') as fd: file_content = fd.read() # Ensure generated file syntax is right compile(file_content, output_script_path, 'exec')
def test_should_handle_notebook_with_invalid_python_name_with_conf(work_dir, mocker): """ Test invalid python filename are converted """ mocked_check_output = mocker.patch('subprocess.check_output', return_value=work_dir.encode()) notebook_path = gen_notebook(cells=[('code', 'pass')], tmp_dir=work_dir, file_name='01_(test) nb.ipynb') # Create conf in a freshly init git repo conf_data = write_conf(work_dir=work_dir, conf_path=join(work_dir, DEFAULT_CONF_FILENAME), ignore_keys=['# Ignore', 'remove=']) cmd_arguments = ['-n', notebook_path] IPynbToPython().run(*cmd_arguments) # This path is generated using the conf script_dir and the notebook name output_script_path = join(work_dir, conf_data['path']['python_script_root_dir'], 'mlvtools_01__test_nb.py') assert exists(output_script_path) with open(output_script_path, 'r') as fd: file_content = fd.read() # Ensure generated file syntax is right compile(file_content, output_script_path, 'exec') assert mocked_check_output.mock_calls == [mocker.call( ['git', 'rev-parse', '--show-toplevel'], cwd=work_dir)]
def test_should_generate_python_script_with_conf_auto_detect(work_dir, mocker): """ Convert a Jupyter Notebook to a Python 3 script using conf """ kept_cells, dropped_cells, docstring, notebook_path = generate_test_notebook( work_dir=work_dir, notebook_name='test_nb.ipynb') # Create conf with knew ignore cell keywords conf_data = write_conf(work_dir=work_dir, conf_path=join(work_dir, DEFAULT_CONF_FILENAME), ignore_keys=['# Ignore', 'import']) cmd_arguments = ['-n', notebook_path, '-w', work_dir] IPynbToPython().run(*cmd_arguments) # This path is generated using the conf script_dir and the notebook name output_script_path = join(work_dir, conf_data['path']['python_script_root_dir'], 'mlvtools_test_nb.py') assert exists(output_script_path) with open(output_script_path, 'r') as fd: file_content = fd.read() # With those knew keywords from conf the first code cell must be ignored (due to import) # The second no effect cell must remain because "# No effect" is no more a keyword assert not is_in(kept_cells.code[0][1], file_content) assert is_in(dropped_cells.no_effect[1][1], file_content)
def test_should_check_consistency_and_exit_with_error_if_same_but_diff_from_conf(work_dir, ref_notebook_path, ref_script_content): """ Test check consistency between a notebook and its script with diff only on blank lines and comments. Should exit with error. Discard a notebook cell changing conf ignore_keys """ conf_path = join(work_dir, 'conf.json') write_conf(work_dir, conf_path, ignore_keys=['# A Tag']) script_path = join(work_dir, 'script.py') with open(script_path, 'w') as fd: fd.write(ref_script_content) arguments = ['-n', ref_notebook_path, '-s', script_path, '--conf-path', conf_path, '--working-directory', work_dir] with pytest.raises(SystemExit) as e: IPynbCheckScript().run(*arguments) assert e.value.code != 0
def test_should_detect_ignore_notebooks(work_dir): """ Test check_all_scripts_consistency exit with error if there is at least one inconsistency """ notebook_dir, script_dir = temporary_setup(work_dir) # Replace the script which correspond to notebook.ipynb with open(join(script_dir, 'mlvtools_notebook.py'), 'w') as fd: fd.write('print("Hello world!")') # Duplicate a notebook with an other name so there is no associated script shutil.copy(join(notebook_dir, 'notebook.ipynb'), join(notebook_dir, 'new_nb.ipynb')) write_conf(work_dir, conf_path=join(work_dir, DEFAULT_CONF_FILENAME), script_dir=script_dir, dvc_cmd_dir=work_dir) # Ignore notebook.ipynb and new_nb.ipynb check_call(['check_all_scripts_consistency', '-n', notebook_dir, '-w', work_dir, '-i', 'notebook.ipynb', '-i', 'new_nb.ipynb'])
def test_should_detect_consistency_for_notebook_and_script_with_conf(work_dir): """ Test check_script_consistency exit without error when there is consistency between notebook and script with conf. Without conf there is inconsistency but with the right ignore cell there is consistency """ notebook_path = join(CURRENT_DIR, 'data', 'notebooks', 'notebook_blank_and_comment_diff.ipynb') script_path = join(CURRENT_DIR, 'data', 'script_no_blank_no_comment_disable_cell.py') write_conf(work_dir, conf_path=join(work_dir, DEFAULT_CONF_FILENAME), ignore_keys=['# A Tag']) check_call([ 'check_script_consistency', '-n', notebook_path, '-s', script_path, '-w', work_dir ])
def test_should_check_consistency_for_nb_from_dir_and_exit_with_error_if_dif_from_conf( work_dir, notebook_dir, script_dir): """ Test check consistency for all notebooks from a given directory. Should exit with error. Discard a notebook cell changing conf ignore_keys """ conf_path = join(work_dir, 'conf.json') write_conf(work_dir, conf_path, script_dir=script_dir, dvc_cmd_dir=work_dir, ignore_keys=['# A Tag']) arguments = [ '-n', notebook_dir, '-c', conf_path, '--working-directory', work_dir ] with pytest.raises(SystemExit) as e: IPynbCheckAllScripts().run(*arguments) assert e.value.code != 0
def test_exit_with_error_if_a_script_is_missing(work_dir, notebook_dir, script_dir): """ Test check consistency exits with error if a script is missing. """ conf_path = join(work_dir, 'conf.json') write_conf(work_dir, conf_path, script_dir=script_dir, dvc_cmd_dir=work_dir) # Remove a valid script remove(join(script_dir, 'mlvtools_bye.py')) arguments = [ '-n', notebook_dir, '-c', conf_path, '--working-directory', work_dir ] with pytest.raises(SystemExit) as e: IPynbCheckAllScripts().run(*arguments) assert e.value.code != 0
def test_exit_without_error_if_a_script_is_inconsistent_but_ignored( work_dir, notebook_dir, script_dir): """ Test check consistency exits without error if inconsistent script is ignored """ conf_path = join(work_dir, 'conf.json') write_conf(work_dir, conf_path, script_dir=script_dir, dvc_cmd_dir=work_dir) # Overwrite one valid script write_script(join(script_dir, 'mlvtools_bye.py'), 'print("A different thing")') arguments = [ '-n', notebook_dir, '-c', conf_path, '--working-directory', work_dir, '-i', 'bye.ipynb' ] with pytest.raises(SystemExit) as e: IPynbCheckAllScripts().run(*arguments) assert e.value.code == 0
def test_should_check_consistency_for_all_notebooks_from_directory_and_exit_with_error( work_dir, notebook_dir, script_dir): """ Test check consistency for all notebooks from a given directory. Should exit with error due to one inconsistency. """ conf_path = join(work_dir, 'conf.json') write_conf(work_dir, conf_path, script_dir=script_dir, dvc_cmd_dir=work_dir) # Overwrite one valid script write_script(join(script_dir, 'mlvtools_bye.py'), 'print("A different thing")') arguments = [ '-n', notebook_dir, '-c', conf_path, '--working-directory', work_dir ] with pytest.raises(SystemExit) as e: IPynbCheckAllScripts().run(*arguments) assert e.value.code != 0
def test_should_convert_to_python_script_using_command_line(work_dir): """ Test ipynb_to_python using command line """ dvc_dir = join(work_dir, 'dvc') script_dir = join(work_dir, 'scripts') write_conf(work_dir, conf_path=join(work_dir, DEFAULT_CONF_FILENAME), script_dir=script_dir, dvc_cmd_dir=dvc_dir) notebook_path = join(CURRENT_DIR, 'data', 'notebook.ipynb') check_call(['ipynb_to_dvc', '-n', notebook_path, '-w', work_dir]) assert exists(script_dir) script_dir_content = glob.glob(join(script_dir, 'mlvtools*.py')) assert len(script_dir_content) == 1 assert stat.S_IMODE(os_stat(script_dir_content[0]).st_mode) == 0o755 assert exists(script_dir) dvc_dir_content = glob.glob(join(dvc_dir, 'mlvtools*_dvc')) assert len(dvc_dir_content) == 1 assert stat.S_IMODE(os_stat(dvc_dir_content[0]).st_mode) == 0o755
def test_should_check_consistency_for_all_notebooks_from_directory_and_exit_without_error( work_dir, notebook_dir, script_dir): """ Test check consistency for all notebooks from a given directory. Should exit without error. Ignore other file from the given directory. """ conf_path = join(work_dir, 'conf.json') write_conf(work_dir, conf_path, script_dir=script_dir, dvc_cmd_dir=work_dir) # Add noise: a text file in notebook directory with open(join(notebook_dir, 'text_file.txt'), 'w') as fd: fd.write('some text') arguments = [ '-n', notebook_dir, '-c', conf_path, '--working-directory', work_dir ] with pytest.raises(SystemExit) as e: IPynbCheckAllScripts().run(*arguments) assert e.value.code == 0
def test_should_load_conf_file(work_dir): """ Test load valid conf file """ conf_file = join(work_dir, '.mlvtools') write_conf(work_dir=work_dir, conf_path=conf_file, ignore_keys=['# No effect', "# Ignore"], script_dir='./scripts', dvc_cmd_dir='./dvc_cmd', dvc_py_cmd_name='VAR_Name', dvc_py_cmd_path='var_PATh3', dvc_meta_file_name='mETA_VAR_NaME') conf = load_conf_or_default(conf_file, working_directory=work_dir) assert conf.path.python_script_root_dir == './scripts' assert conf.path.dvc_cmd_root_dir == './dvc_cmd' assert '# No effect' in conf.ignore_keys assert '# Ignore' in conf.ignore_keys assert conf.dvc_var_python_cmd_path == 'var_PATh3' assert conf.dvc_var_python_cmd_name == 'VAR_Name' assert conf.dvc_var_meta_filename == 'mETA_VAR_NaME' script_path = join(conf.path.python_script_root_dir, 'mlvtools_pipeline_part1.py') assert get_script_output_path('./data/Pipeline Part1.ipynb', conf) == join(work_dir, script_path) dvc_cmd_path = join(conf.path.dvc_cmd_root_dir, 'pipeline_part1_dvc') assert get_dvc_cmd_output_path('./data/pipeline_part1.py', conf) == join(work_dir, dvc_cmd_path)
def setup_with_conf(work_dir: str, conf_path: str) -> Tuple[str, str]: write_conf(work_dir=work_dir, conf_path=conf_path, script_dir='./test_scripts') nb_path = gen_notebook([('markdown', '# test')], work_dir, 'nb_test.ipynb') return conf_path, nb_path