From e550c146daf34e8fb697760be3c03b4db5816c65 2009-04-03 06:43:29 From: Fernando Perez Date: 2009-04-03 06:43:29 Subject: [PATCH] Make user_setup a top-level function in iplib and add better tests. This function has grown way too big and it needs a refactoring, but at least now it's a standalone thing (it didn't need to be a method) and can be called in a non-interactive fashion. --- diff --git a/IPython/iplib.py b/IPython/iplib.py index 90f2c38..ef2f044 100644 --- a/IPython/iplib.py +++ b/IPython/iplib.py @@ -108,6 +108,197 @@ def softspace(file, newvalue): return oldvalue +def user_setup(ipythondir,rc_suffix,mode='install',interactive=True): + """Install or upgrade the user configuration directory. + + Can be called when running for the first time or to upgrade the user's + .ipython/ directory. + + Parameters + ---------- + ipythondir : path + The directory to be used for installation/upgrade. In 'install' mode, + if this path already exists, the function exits immediately. + + rc_suffix : str + Extension for the config files. On *nix platforms it is typically the + empty string, while Windows normally uses '.ini'. + + mode : str, optional + Valid modes are 'install' and 'upgrade'. + + interactive : bool, optional + If False, do not wait for user input on any errors. Normally after + printing its status information, this function waits for the user to + hit Return before proceeding. This is because the default use case is + when first installing the IPython configuration, so we want the user to + acknowledge the initial message, which contains some useful + information. + """ + + # For automatic use, deactivate all i/o + if interactive: + def wait(): + try: + raw_input("Please press to start IPython.") + except EOFError: + print >> Term.cout + print '*'*70 + + def printf(s): + print s + else: + wait = lambda : None + printf = lambda s : None + + # Install mode should be re-entrant: if the install dir already exists, + # bail out cleanly + if mode == 'install' and os.path.isdir(ipythondir): + return + + cwd = os.getcwd() # remember where we started + glb = glob.glob + + printf('*'*70) + if mode == 'install': + printf( +"""Welcome to IPython. I will try to create a personal configuration directory +where you can customize many aspects of IPython's functionality in:\n""") + else: + printf('I am going to upgrade your configuration in:') + + printf(ipythondir) + + rcdirend = os.path.join('IPython','UserConfig') + cfg = lambda d: os.path.join(d,rcdirend) + try: + rcdir = filter(os.path.isdir,map(cfg,sys.path))[0] + printf("Initializing from configuration: %s" % rcdir) + except IndexError: + warning = """ +Installation error. IPython's directory was not found. + +Check the following: + +The ipython/IPython directory should be in a directory belonging to your +PYTHONPATH environment variable (that is, it should be in a directory +belonging to sys.path). You can copy it explicitly there or just link to it. + +IPython will create a minimal default configuration for you. + +""" + warn(warning) + wait() + + if sys.platform =='win32': + inif = 'ipythonrc.ini' + else: + inif = 'ipythonrc' + minimal_setup = {'ipy_user_conf.py' : 'import ipy_defaults', + inif : '# intentionally left blank' } + os.makedirs(ipythondir, mode = 0777) + for f, cont in minimal_setup.items(): + # In 2.5, this can be more cleanly done using 'with' + fobj = file(ipythondir + '/' + f,'w') + fobj.write(cont) + fobj.close() + + return + + if mode == 'install': + try: + shutil.copytree(rcdir,ipythondir) + os.chdir(ipythondir) + rc_files = glb("ipythonrc*") + for rc_file in rc_files: + os.rename(rc_file,rc_file+rc_suffix) + except: + warning = """ + +There was a problem with the installation: +%s +Try to correct it or contact the developers if you think it's a bug. +IPython will proceed with builtin defaults.""" % sys.exc_info()[1] + warn(warning) + wait() + return + + elif mode == 'upgrade': + try: + os.chdir(ipythondir) + except: + printf(""" +Can not upgrade: changing to directory %s failed. Details: +%s +""" % (ipythondir,sys.exc_info()[1]) ) + wait() + return + else: + sources = glb(os.path.join(rcdir,'[A-Za-z]*')) + for new_full_path in sources: + new_filename = os.path.basename(new_full_path) + if new_filename.startswith('ipythonrc'): + new_filename = new_filename + rc_suffix + # The config directory should only contain files, skip any + # directories which may be there (like CVS) + if os.path.isdir(new_full_path): + continue + if os.path.exists(new_filename): + old_file = new_filename+'.old' + if os.path.exists(old_file): + os.remove(old_file) + os.rename(new_filename,old_file) + shutil.copy(new_full_path,new_filename) + else: + raise ValueError('unrecognized mode for install: %r' % mode) + + # Fix line-endings to those native to each platform in the config + # directory. + try: + os.chdir(ipythondir) + except: + printf(""" +Problem: changing to directory %s failed. +Details: +%s + +Some configuration files may have incorrect line endings. This should not +cause any problems during execution. """ % (ipythondir,sys.exc_info()[1]) ) + wait() + else: + for fname in glb('ipythonrc*'): + try: + native_line_ends(fname,backup=0) + except IOError: + pass + + if mode == 'install': + printf(""" +Successful installation! + +Please read the sections 'Initial Configuration' and 'Quick Tips' in the +IPython manual (there are both HTML and PDF versions supplied with the +distribution) to make sure that your system environment is properly configured +to take advantage of IPython's features. + +Important note: the configuration system has changed! The old system is +still in place, but its setting may be partly overridden by the settings in +"~/.ipython/ipy_user_conf.py" config file. Please take a look at the file +if some of the new settings bother you. + +""") + else: + printf(""" +Successful upgrade! + +All files in your directory: +%(ipythondir)s +which would have been overwritten by the upgrade were backed up with a .old +extension. If you had made particular customizations in those files you may +want to merge them back into the new files.""" % locals() ) + wait() + os.chdir(cwd) + #**************************************************************************** # Local use exceptions class SpaceInInput(exceptions.Exception): pass @@ -1114,161 +1305,11 @@ class InteractiveShell(object,Magic): def user_setup(self,ipythondir,rc_suffix,mode='install'): """Install the user configuration directory. - Can be called when running for the first time or to upgrade the user's - .ipython/ directory with the mode parameter. Valid modes are 'install' - and 'upgrade'.""" - - def wait(): - try: - raw_input("Please press to start IPython.") - except EOFError: - print >> Term.cout - print '*'*70 - - # Install mode should be re-entrant: if the install dir already exists, - # bail out cleanly - if mode == 'install' and os.path.isdir(ipythondir): - return - - cwd = os.getcwd() # remember where we started - glb = glob.glob - print '*'*70 - if mode == 'install': - print \ -"""Welcome to IPython. I will try to create a personal configuration directory -where you can customize many aspects of IPython's functionality in:\n""" - else: - print 'I am going to upgrade your configuration in:' - - print ipythondir - - rcdirend = os.path.join('IPython','UserConfig') - cfg = lambda d: os.path.join(d,rcdirend) - try: - rcdir = filter(os.path.isdir,map(cfg,sys.path))[0] - print "Initializing from configuration",rcdir - except IndexError: - warning = """ -Installation error. IPython's directory was not found. - -Check the following: - -The ipython/IPython directory should be in a directory belonging to your -PYTHONPATH environment variable (that is, it should be in a directory -belonging to sys.path). You can copy it explicitly there or just link to it. - -IPython will create a minimal default configuration for you. - -""" - warn(warning) - wait() - - if sys.platform =='win32': - inif = 'ipythonrc.ini' - else: - inif = 'ipythonrc' - minimal_setup = {'ipy_user_conf.py' : 'import ipy_defaults', - inif : '# intentionally left blank' } - os.makedirs(ipythondir, mode = 0777) - for f, cont in minimal_setup.items(): - open(ipythondir + '/' + f,'w').write(cont) - - return - - if mode == 'install': - try: - shutil.copytree(rcdir,ipythondir) - os.chdir(ipythondir) - rc_files = glb("ipythonrc*") - for rc_file in rc_files: - os.rename(rc_file,rc_file+rc_suffix) - except: - warning = """ - -There was a problem with the installation: -%s -Try to correct it or contact the developers if you think it's a bug. -IPython will proceed with builtin defaults.""" % sys.exc_info()[1] - warn(warning) - wait() - return - - elif mode == 'upgrade': - try: - os.chdir(ipythondir) - except: - print """ -Can not upgrade: changing to directory %s failed. Details: -%s -""" % (ipythondir,sys.exc_info()[1]) - wait() - return - else: - sources = glb(os.path.join(rcdir,'[A-Za-z]*')) - for new_full_path in sources: - new_filename = os.path.basename(new_full_path) - if new_filename.startswith('ipythonrc'): - new_filename = new_filename + rc_suffix - # The config directory should only contain files, skip any - # directories which may be there (like CVS) - if os.path.isdir(new_full_path): - continue - if os.path.exists(new_filename): - old_file = new_filename+'.old' - if os.path.exists(old_file): - os.remove(old_file) - os.rename(new_filename,old_file) - shutil.copy(new_full_path,new_filename) - else: - raise ValueError,'unrecognized mode for install:',`mode` - - # Fix line-endings to those native to each platform in the config - # directory. - try: - os.chdir(ipythondir) - except: - print """ -Problem: changing to directory %s failed. -Details: -%s - -Some configuration files may have incorrect line endings. This should not -cause any problems during execution. """ % (ipythondir,sys.exc_info()[1]) - wait() - else: - for fname in glb('ipythonrc*'): - try: - native_line_ends(fname,backup=0) - except IOError: - pass - - if mode == 'install': - print """ -Successful installation! - -Please read the sections 'Initial Configuration' and 'Quick Tips' in the -IPython manual (there are both HTML and PDF versions supplied with the -distribution) to make sure that your system environment is properly configured -to take advantage of IPython's features. - -Important note: the configuration system has changed! The old system is -still in place, but its setting may be partly overridden by the settings in -"~/.ipython/ipy_user_conf.py" config file. Please take a look at the file -if some of the new settings bother you. - -""" - else: - print """ -Successful upgrade! - -All files in your directory: -%(ipythondir)s -which would have been overwritten by the upgrade were backed up with a .old -extension. If you had made particular customizations in those files you may -want to merge them back into the new files.""" % locals() - wait() - os.chdir(cwd) - # end user_setup() + Note + ---- + DEPRECATED: use the top-level user_setup() function instead. + """ + return user_setup(ipythondir,rc_suffix,mode) def atexit_operations(self): """This will be executed at the time of exit. diff --git a/IPython/tests/test_iplib.py b/IPython/tests/test_iplib.py index ce33d81..f0d45e5 100644 --- a/IPython/tests/test_iplib.py +++ b/IPython/tests/test_iplib.py @@ -1,12 +1,39 @@ """Tests for the key iplib module, where the main ipython class is defined. """ +#----------------------------------------------------------------------------- +# Module imports +#----------------------------------------------------------------------------- +# stdlib +import os +import shutil +import tempfile + +# third party import nose.tools as nt -# Useful global ipapi object and main IPython one -ip = _ip -IP = ip.IP +# our own packages +from IPython import iplib + +#----------------------------------------------------------------------------- +# Globals +#----------------------------------------------------------------------------- + +# Useful global ipapi object and main IPython one. Unfortunately we have a +# long precedent of carrying the 'ipapi' global object which is injected into +# the system namespace as _ip, but that keeps a pointer to the actual IPython +# InteractiveShell instance, which is named IP. Since in testing we do need +# access to the real thing (we want to probe beyond what ipapi exposes), make +# here a global reference to each. In general, things that are exposed by the +# ipapi instance should be read from there, but we also will often need to use +# the actual IPython one. +ip = _ip # This is the ipapi instance +IP = ip.IP # This is the actual IPython shell 'raw' object. + +#----------------------------------------------------------------------------- +# Test functions +#----------------------------------------------------------------------------- def test_reset(): """reset must clear most namespaces.""" @@ -20,8 +47,22 @@ def test_reset(): nt.assert_equals(len(ns),0) +# make sure that user_setup can be run re-entrantly in 'install' mode. def test_user_setup(): - """make sure that user_setup can be run re-entrantly in 'install' mode. - """ - # This should basically run without errors or output. - IP.user_setup(ip.options.ipythondir,'','install') + # use a lambda to pass kwargs to the generator + user_setup = lambda a,k: iplib.user_setup(*a,**k) + kw = dict(mode='install',interactive=False) + + # Call the user setup and verify that the directory exists + yield user_setup, (ip.options.ipythondir,''),kw + yield os.path.isdir,ip.options.ipythondir + + # Now repeat the operation with a non-existent directory. Check both that + # the call succeeds and that the directory is created. + tmpdir = tempfile.mktemp(prefix='ipython-test-') + try: + yield user_setup, (tmpdir,''),kw + yield os.path.isdir,tmpdir + finally: + # In this case, clean up the temp dir once done + shutil.rmtree(tmpdir)