123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- from __future__ import unicode_literals
- """Migrating IPython < 4.0 to Jupyter
- This *copies* configuration and resources to their new locations in Jupyter
- Migrations:
- - .ipython/
- - nbextensions -> JUPYTER_DATA_DIR/nbextensions
- - kernels -> JUPYTER_DATA_DIR/kernels
- - .ipython/profile_default/
- - static/custom -> .jupyter/custom
- - nbconfig -> .jupyter/nbconfig
- - security/
- - notebook_secret, notebook_cookie_secret, nbsignatures.db -> JUPYTER_DATA_DIR
- - ipython_{notebook,nbconvert,qtconsole}_config.py -> .jupyter/jupyter_{name}_config.py
- """
- # Copyright (c) Jupyter Development Team.
- # Distributed under the terms of the Modified BSD License.
- import os
- import re
- import shutil
- from datetime import datetime
- from traitlets.config import PyFileConfigLoader, JSONFileConfigLoader
- from traitlets.log import get_logger
- from .utils import ensure_dir_exists
- from .paths import jupyter_config_dir, jupyter_data_dir
- from .application import JupyterApp
- pjoin = os.path.join
- migrations = {
- pjoin('{ipython_dir}', 'nbextensions'): pjoin('{jupyter_data}', 'nbextensions'),
- pjoin('{ipython_dir}', 'kernels'): pjoin('{jupyter_data}', 'kernels'),
- pjoin('{profile}', 'nbconfig'): pjoin('{jupyter_config}', 'nbconfig'),
- }
- custom_src_t = pjoin('{profile}', 'static', 'custom')
- custom_dst_t = pjoin('{jupyter_config}', 'custom')
- for security_file in ('notebook_secret', 'notebook_cookie_secret', 'nbsignatures.db'):
- src = pjoin('{profile}', 'security', security_file)
- dst = pjoin('{jupyter_data}', security_file)
- migrations[src] = dst
- config_migrations = ['notebook', 'nbconvert', 'qtconsole']
- regex = re.compile
- config_substitutions = {
- regex(r'\bIPythonQtConsoleApp\b'): 'JupyterQtConsoleApp',
- regex(r'\bIPythonWidget\b'): 'JupyterWidget',
- regex(r'\bRichIPythonWidget\b'): 'RichJupyterWidget',
- regex(r'\bIPython\.html\b'): 'notebook',
- regex(r'\bIPython\.nbconvert\b'): 'nbconvert',
- }
- def get_ipython_dir():
- """Return the IPython directory location.
- Not imported from IPython because the IPython implementation
- ensures that a writable directory exists,
- creating a temporary directory if not.
- We don't want to trigger that when checking if migration should happen.
- We only need to support the IPython < 4 behavior for migration,
- so importing for forward-compatibility and edge cases is not important.
- """
- return os.environ.get('IPYTHONDIR', os.path.expanduser('~/.ipython'))
- def migrate_dir(src, dst):
- """Migrate a directory from src to dst"""
- log = get_logger()
- if not os.listdir(src):
- log.debug("No files in %s" % src)
- return False
- if os.path.exists(dst):
- if os.listdir(dst):
- # already exists, non-empty
- log.debug("%s already exists" % dst)
- return False
- else:
- os.rmdir(dst)
- log.info("Copying %s -> %s" % (src, dst))
- ensure_dir_exists(os.path.dirname(dst))
- shutil.copytree(src, dst, symlinks=True)
- return True
- def migrate_file(src, dst, substitutions=None):
- """Migrate a single file from src to dst
-
- substitutions is an optional dict of {regex: replacement} for performing replacements on the file.
- """
- log = get_logger()
- if os.path.exists(dst):
- # already exists
- log.debug("%s already exists" % dst)
- return False
- log.info("Copying %s -> %s" % (src, dst))
- ensure_dir_exists(os.path.dirname(dst))
- shutil.copy(src, dst)
- if substitutions:
- with open(dst) as f:
- text = f.read()
- for pat, replacement in substitutions.items():
- text = pat.sub(replacement, text)
- with open(dst, 'w') as f:
- f.write(text)
- return True
- def migrate_one(src, dst):
- """Migrate one item
-
- dispatches to migrate_dir/_file
- """
- log = get_logger()
- if os.path.isfile(src):
- return migrate_file(src, dst)
- elif os.path.isdir(src):
- return migrate_dir(src, dst)
- else:
- log.debug("Nothing to migrate for %s" % src)
- return False
- def migrate_static_custom(src, dst):
- """Migrate non-empty custom.js,css from src to dst
-
- src, dst are 'custom' directories containing custom.{js,css}
- """
- log = get_logger()
- migrated = False
-
- custom_js = pjoin(src, 'custom.js')
- custom_css = pjoin(src, 'custom.css')
- # check if custom_js is empty:
- custom_js_empty = True
- if os.path.isfile(custom_js):
- with open(custom_js) as f:
- js = f.read().strip()
- for line in js.splitlines():
- if not (
- line.isspace()
- or line.strip().startswith(('/*', '*', '//'))
- ):
- custom_js_empty = False
- break
-
- # check if custom_css is empty:
- custom_css_empty = True
- if os.path.isfile(custom_css):
- with open(custom_css) as f:
- css = f.read().strip()
- custom_css_empty = css.startswith('/*') and css.endswith('*/')
-
- if custom_js_empty:
- log.debug("Ignoring empty %s" % custom_js)
- if custom_css_empty:
- log.debug("Ignoring empty %s" % custom_css)
-
- if custom_js_empty and custom_css_empty:
- # nothing to migrate
- return False
- ensure_dir_exists(dst)
-
- if not custom_js_empty or not custom_css_empty:
- ensure_dir_exists(dst)
-
- if not custom_js_empty:
- if migrate_file(custom_js, pjoin(dst, 'custom.js')):
- migrated = True
- if not custom_css_empty:
- if migrate_file(custom_css, pjoin(dst, 'custom.css')):
- migrated = True
-
- return migrated
- def migrate_config(name, env):
- """Migrate a config file
-
- Includes substitutions for updated configurable names.
- """
- log = get_logger()
- src_base = pjoin('{profile}', 'ipython_{name}_config').format(name=name, **env)
- dst_base = pjoin('{jupyter_config}', 'jupyter_{name}_config').format(name=name, **env)
- loaders = {
- '.py': PyFileConfigLoader,
- '.json': JSONFileConfigLoader,
- }
- migrated = []
- for ext in ('.py', '.json'):
- src = src_base + ext
- dst = dst_base + ext
- if os.path.exists(src):
- cfg = loaders[ext](src).load_config()
- if cfg:
- if migrate_file(src, dst, substitutions=config_substitutions):
- migrated.append(src)
- else:
- # don't migrate empty config files
- log.debug("Not migrating empty config file: %s" % src)
- return migrated
- def migrate():
- """Migrate IPython configuration to Jupyter"""
- env = {
- 'jupyter_data': jupyter_data_dir(),
- 'jupyter_config': jupyter_config_dir(),
- 'ipython_dir': get_ipython_dir(),
- 'profile': os.path.join(get_ipython_dir(), 'profile_default'),
- }
- migrated = False
- for src_t, dst_t in migrations.items():
- src = src_t.format(**env)
- dst = dst_t.format(**env)
- if os.path.exists(src):
- if migrate_one(src, dst):
- migrated = True
-
- for name in config_migrations:
- if migrate_config(name, env):
- migrated = True
-
- custom_src = custom_src_t.format(**env)
- custom_dst = custom_dst_t.format(**env)
-
- if os.path.exists(custom_src):
- if migrate_static_custom(custom_src, custom_dst):
- migrated = True
-
- # write a marker to avoid re-running migration checks
- ensure_dir_exists(env['jupyter_config'])
- with open(os.path.join(env['jupyter_config'], 'migrated'), 'w') as f:
- f.write(datetime.utcnow().isoformat())
-
- return migrated
- class JupyterMigrate(JupyterApp):
- name = 'jupyter-migrate'
- description = """
- Migrate configuration and data from .ipython prior to 4.0 to Jupyter locations.
-
- This migrates:
-
- - config files in the default profile
- - kernels in ~/.ipython/kernels
- - notebook javascript extensions in ~/.ipython/extensions
- - custom.js/css to .jupyter/custom
-
- to their new Jupyter locations.
-
- All files are copied, not moved.
- If the destinations already exist, nothing will be done.
- """
-
- def start(self):
- if not migrate():
- self.log.info("Found nothing to migrate.")
- main = JupyterMigrate.launch_instance
- if __name__ == '__main__':
- main()
|