diff --git a/nbconvert.py b/nbconvert.py index 9d70d42..cf540a8 100755 --- a/nbconvert.py +++ b/nbconvert.py @@ -1,5 +1,4 @@ #!/usr/bin/env python - """NBConvert is a utility for conversion of IPYNB files. Commandline interface for the NBConvert conversion utility. Read the @@ -26,15 +25,16 @@ import os #From IPython #All the stuff needed for the configurable things from IPython.config.application import Application +from IPython.utils.traitlets import (Bool) #Local imports from nbconvert.api.convert import export_by_name from nbconvert.api.exporter import Exporter +from nbconvert.transformers import extractfigure #----------------------------------------------------------------------------- #Globals and constants #----------------------------------------------------------------------------- -NBCONVERT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(__file__))) #'Keys in resources' user prompt. KEYS_PROMPT_HEAD = "====================== Keys in Resources ==================================" @@ -46,56 +46,62 @@ the 'write' (boolean) flag of the converter. =========================================================================== """ -#Error Messages -ERROR_CONFIG_NOT_FOUND = "Config file for profile '%s' not found, giving up." - #----------------------------------------------------------------------------- #Classes and functions #----------------------------------------------------------------------------- -class NbconvertApp(Application): - """A basic application to convert ipynb files""" - - aliases = { - 'stdout':'NbconvertApp.stdout', - 'write':'NbconvertApp.write' - } - - flags = {} - flags['no-stdout'] = ( - {'NbconvertApp' : {'stdout' : False}}, - """Do not print converted file to stdout, equivalent to - --stdout=False""" + +class NbConvertApp(Application): + """Application used to convert to and from notebook file type (*.ipynb)""" + + stdout = Bool( + True, config=True, + help="""Whether to print the converted IPYNB file to stdout + use full do diff files without actually writing a new file""" ) + write = Bool( + False, config=True, + help="""Should the converted notebook file be written to disk + along with potential extracted resources.""" + ) + + def __init__(self, **kwargs): """Public constructor""" #Call base class - super(NbconvertApp, self).__init__(**kwargs) + super(NbConvertApp, self).__init__(**kwargs) #Register class here to have help with help all self.classes.insert(0, Exporter) def start(self, argv=None): - """Convert a notebook in one step""" - + """Entrypoint of NbConvert application. + + Parameters + ---------- + argv : list + Commandline arguments + """ + #Parse the commandline options. self.parse_command_line(argv) #Call base - super(NbconvertApp, self).start() + super(NbConvertApp, self).start() - #The last arguments in chain of arguments will be used as conversion type - ipynb_file = (self.extra_args)[2] + #The last arguments in list will be used by nbconvert export_type = (self.extra_args)[1] + ipynb_file = (self.extra_args)[2] #Export output, resources, exporter = export_by_name(ipynb_file, export_type) + #TODO: Allow user to set output directory and file. destination_filename = None destination_directory = None - if exporter.write: + if self.write: #Get the file name without the '.ipynb' (6 chars) extension and then #remove any addition periods and spaces. The resulting name will @@ -109,11 +115,33 @@ class NbconvertApp(Application): os.mkdir(destination_directory) #Write the results - if exporter.stdout or exporter.write: - self._write_results(exporter.stdout, destination_filename, destination_directory, output, resources) + if self.stdout or not (destination_filename is None and destination_directory is None): + self._write_results(output, resources, self.stdout, destination_filename, destination_directory) - def _write_results(self, stdout, destination_filename, destination_directory, output, resources): + def _write_results(self, output, resources, stdout=False, destination_filename=None, destination_directory=None): + """Output the conversion results to the console and/or filesystem + + Parameters + ---------- + output : str + Output of conversion + resources : dictionary + Additional input/output used by the transformers. For + example, the ExtractFigure transformer outputs the + figures it extracts into this dictionary. This method + relies on the figures being in this dictionary when + attempting to write the figures to the file system. + stdout : bool, Optional + Whether or not to echo output to console + destination_filename : str, Optional + Filename to write output into. If None, output is not + written to a file. + destination_directory : str, Optional + Directory to write notebook data (i.e. figures) to. If + None, figures are not written to the file system. + """ + if stdout: print(output.encode('utf-8')) @@ -122,34 +150,41 @@ class NbconvertApp(Application): with io.open(destination_filename, 'w') as f: f.write(output) + #Get the key names used by the extract figure transformer + figures_key = extractfigure.FIGURES_KEY + binary_key = extractfigure.BINARY_KEY + text_key = extractfigure.TEXT_KEY + #Output any associate figures into the same "root" directory. - binkeys = resources.get('figures', {}).get('binary',{}).keys() - textkeys = resources.get('figures', {}).get('text',{}).keys() + binkeys = resources.get(figures_key, {}).get(binary_key,{}).keys() + textkeys = resources.get(figures_key, {}).get(text_key,{}).keys() if binkeys or textkeys : if not destination_directory is None: for key in binkeys: with io.open(os.path.join(destination_directory, key), 'wb') as f: - f.write(resources['figures']['binary'][key]) + f.write(resources[figures_key][binary_key][key]) for key in textkeys: with io.open(os.path.join(destination_directory, key), 'w') as f: - f.write(resources['figures']['text'][key]) + f.write(resources[figures_key][text_key][key]) #Figures that weren't exported which will need to be created by the #user. Tell the user what figures these are. if stdout: print(KEYS_PROMPT_HEAD) - print(resources['figures'].keys()) + print(resources[figures_key].keys()) print(KEYS_PROMPT_BODY) #----------------------------------------------------------------------------- #Script main #----------------------------------------------------------------------------- + def main(): - """Convert a notebook in one step""" + """Application entry point""" - app = NbconvertApp.instance() + app = NbConvertApp.instance() app.description = __doc__ app.start(argv=sys.argv) +#Check to see if python is calling this file directly. if __name__ == '__main__': main() \ No newline at end of file diff --git a/nbconvert/utils/console.py b/nbconvert/utils/console.py new file mode 100644 index 0000000..abd6741 --- /dev/null +++ b/nbconvert/utils/console.py @@ -0,0 +1,121 @@ +"""Utility functions for interacting with the console""" +#----------------------------------------------------------------------------- +# Copyright (c) 2013, the IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Used to determine python version +import sys + +#----------------------------------------------------------------------------- +# Classes and functions +#----------------------------------------------------------------------------- + +def input(self, prompt_text): + """ + Prompt the user for input. + + The input command will change depending on the version of python + installed. To maintain support for 2 and earlier, we must use + raw_input in that case. Else use input. + + Parameters + ---------- + prompt_text : str + Prompt to display to the user. + """ + + # Try to get the python version. This command is only available in + # python 2 and later, so it's important that we catch the exception + # if the command isn't found. + try: + majorversion = sys.version_info[0] + except: + majorversion = 1 + + # Use the correct function to prompt the user for input depending on + # what python version the code is running in. + if majorversion >= 3: + return input(prompt_text) + else: + return raw_input(prompt_text) + + +def prompt_boolean(self, prompt, default=False): + """ + Prompt the user for a boolean response. + + Parameters + ---------- + prompt : str + prompt to display to the user + default : bool, optional + response to return if none is given by the user + """ + + response = self._input(prompt) + response = response.strip().lower() + + #Catch 1, true, yes as True + if len(response) > 0 and (response == "1" or response[0] == "t" or response[0] == "y"): + return True + + #Catch 0, false, no as False + elif len(response) > 0 and (response == "0" or response[0] == "f" or response[0] == "n"): + return False + + else: + return default + + +def prompt_dictionary(self, choices, default_style=1, menu_comments={}): + """ + Prompt the user to chose one of many selections from a menu. + + Parameters + ---------- + choices : dictionary + Keys - choice numbers (int) + Values - choice value (str), this is what the function will return + default_style : int, optional + Choice to select if the user doesn't respond + menu_comments : dictionary, optional + Additional comments to append to the menu as it is displayed + in the console. + Keys - choice numbers (int) + Values - comment (str), what will be appended to the + corresponding choice + """ + + # Build the menu that will be displayed to the user with + # all of the options available. + prompt = "" + for key, value in choices.iteritems(): + prompt += "%d %s " % (key, value) + if key in menu_comments: + prompt += menu_comments[key] + prompt += "\n" + + # Continue to ask the user for a style until an appropriate + # one is specified. + response = -1 + while (not response in choices): + try: + text_response = self._input(prompt) + + # Use default option if no input. + if len(text_response.strip()) == 0: + response = default_style + else: + response = int(text_response) + except: + print("Error: Value is not an available option. 0 selects the default.\n") + return choices[response] + \ No newline at end of file diff --git a/nbconvert/utils/exceptions.py b/nbconvert/utils/exceptions.py index f1904fa..6a4e0e6 100644 --- a/nbconvert/utils/exceptions.py +++ b/nbconvert/utils/exceptions.py @@ -1,4 +1,4 @@ -"""Contains all of the exceptions used in NBConvert explicitly""" +"""NbConvert specific exceptions""" #----------------------------------------------------------------------------- # Copyright (c) 2013, the IPython Development Team. # @@ -8,13 +8,9 @@ #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- -from __future__ import print_function - -#----------------------------------------------------------------------------- # Classes and functions #----------------------------------------------------------------------------- + class ConversionException(Exception): """An exception raised by the conversion process.""" diff --git a/nbconvert/utils/lexers.py b/nbconvert/utils/lexers.py index 3d5e1a6..9a9092e 100644 --- a/nbconvert/utils/lexers.py +++ b/nbconvert/utils/lexers.py @@ -4,7 +4,7 @@ Informs The pygments highlighting library of the quirks of IPython's superset of Python -- magic commands, !shell commands, etc. """ #----------------------------------------------------------------------------- -# Copyright (c) 2012, the IPython Development Team. +# Copyright (c) 2013, the IPython Development Team. # # Distributed under the terms of the Modified BSD License. # @@ -20,19 +20,27 @@ from pygments.lexers import PythonLexer, BashLexer from pygments.lexer import bygroups, using from pygments.token import Keyword, Operator, Text - #----------------------------------------------------------------------------- # Class declarations #----------------------------------------------------------------------------- class IPythonLexer(PythonLexer): + """ + Pygments Lexer for use with IPython code. Inherits from + PythonLexer and adds information about IPython specific + keywords (i.e. magic commands, shell commands, etc.) + """ + + #Basic properties name = 'IPython' aliases = ['ip', 'ipython'] filenames = ['*.ipy'] + + #Highlighting information tokens = PythonLexer.tokens.copy() tokens['root'] = [ (r'(\%+)(\w+)\s+(\.*)(\n)', bygroups(Operator, Keyword, using(BashLexer), Text)), (r'(\%+)(\w+)\b', bygroups(Operator, Keyword)), (r'^(!)(.+)(\n)', bygroups(Operator, using(BashLexer), Text)), - ] + tokens['root'] + ] + tokens['root']