Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

migrate config at start of applications #25

Merged
merged 6 commits into from
Jun 25, 2015
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions jupyter_core/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ class JupyterApp(Application):
aliases = base_aliases
flags = base_flags

def _log_level_default(self):
return logging.INFO

jupyter_path = List(Unicode)
def _jupyter_path_default(self):
return jupyter_path()
Expand Down Expand Up @@ -143,6 +146,20 @@ def ask():
with open(config_file, mode='w') as f:
f.write(config_text)

def migrate_config(self):
"""Migrate config/data from IPython 3"""
if os.path.exists(os.path.join(self.config_dir, 'migrated')):
# already migrated
return

ipdir = os.environ.get('IPYTHONDIR') or os.path.expanduser('~/.ipython')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're importing/defining get_ipython_dir in the migrate module, should this import and use that rather than copying only the fallback logic?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense. Done.

# No IPython dir, nothing to migrate
if not os.path.exists(ipdir):
return

from .migrate import migrate
migrate()

def load_config_file(self, suppress_errors=True):
"""Load the config file.

Expand Down Expand Up @@ -215,6 +232,7 @@ def initialize(self, argv=None):
cl_config = self.config
if self._dispatching:
return
self.migrate_config()
self.load_config_file()
# enforce cl-opts override configfile opts:
self.update_config(cl_config)
Expand Down
57 changes: 40 additions & 17 deletions jupyter_core/migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,21 @@
import os
import re
import shutil
from datetime import datetime

from traitlets.config import PyFileConfigLoader, JSONFileConfigLoader
from traitlets.log import get_logger

from ipython_genutils.path import ensure_dir_exists
try:
from IPython.paths import get_ipython_dir, locate_profile
from IPython.paths import get_ipython_dir
except ImportError:
# IPython < 4
from IPython.utils.path import get_ipython_dir, locate_profile
try:
from IPython.utils.path import get_ipython_dir
except ImportError:
def get_ipython_dir():
return os.environ.get('IPYTHONDIR', os.path.expanduser('~/.ipython'))

from .paths import jupyter_config_dir, jupyter_data_dir
from .application import JupyterApp
Expand Down Expand Up @@ -64,17 +70,18 @@

def migrate_dir(src, dst):
"""Migrate a directory from src to dst"""
log = get_logger()
if not os.listdir(src):
print("No files in %s" % src)
log.debug("No files in %s" % src)
return False
if os.path.exists(dst):
if os.listdir(dst):
# already exists, non-empty
print("%s already exists" % dst)
log.debug("%s already exists" % dst)
return False
else:
os.rmdir(dst)
print("Copying %s -> %s" % (src, dst))
log.info("Copying %s -> %s" % (src, dst))
ensure_dir_exists(os.path.dirname(dst))
shutil.copytree(src, dst, symlinks=True)
return True
Expand All @@ -85,11 +92,12 @@ def migrate_file(src, dst, substitutions=None):

substitutions is an optional dict of {regex: replacement} for performing replacements on the file.
"""
log = get_logger()
if os.path.exists(dst):
# already exists
print("%s already exists" % dst)
log.debug("%s already exists" % dst)
return False
print("Copying %s -> %s" % (src, dst))
log.info("Copying %s -> %s" % (src, dst))
ensure_dir_exists(os.path.dirname(dst))
shutil.copy(src, dst)
if substitutions:
Expand All @@ -107,12 +115,13 @@ def migrate_one(src, dst):

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:
print("Nothing to migrate for %s" % src)
log.debug("Nothing to migrate for %s" % src)
return False


Expand All @@ -121,6 +130,7 @@ def migrate_static_custom(src, dst):

src, dst are 'custom' directories containing custom.{js,css}
"""
log = get_logger()
migrated = False

custom_js = pjoin(src, 'custom.js')
Expand All @@ -142,9 +152,9 @@ def migrate_static_custom(src, dst):
custom_css_empty = css.startswith('/*') and css.endswith('*/')

if custom_js_empty:
print("Ignoring empty %s" % custom_js)
log.debug("Ignoring empty %s" % custom_js)
if custom_css_empty:
print("Ignoring empty %s" % custom_css)
log.debug("Ignoring empty %s" % custom_css)

if custom_js_empty and custom_css_empty:
# nothing to migrate
Expand All @@ -169,6 +179,7 @@ def migrate_config(name, env):

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 = {
Expand All @@ -186,7 +197,7 @@ def migrate_config(name, env):
migrated.append(src)
else:
# don't migrate empty config files
print("Not migrating empty config file: %s" % src)
log.debug("Not migrating empty config file: %s" % src)
return migrated


Expand All @@ -198,24 +209,35 @@ def migrate():
'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):
pass
migrated = True

for name in config_migrations:
migrate_config(name, env)
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):
migrate_static_custom(custom_src, custom_dst)
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.now().isoformat())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't look like we're using this timestamp anywhere, but in case we later decide to, should it be utcnow()? If the user changes time zone between creating and using the timestamp, calculations based on local time will be off.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utc is always a good idea. Done.


return migrated



class MigrateApp(JupyterApp):
class JupyterMigrate(JupyterApp):
name = 'jupyter-migrate'
description = """
Migrate configuration and data from .ipython prior to 4.0 to Jupyter locations.
Expand All @@ -234,10 +256,11 @@ class MigrateApp(JupyterApp):
"""

def start(self):
migrate()
if not migrate():
self.log.info("Found nothing to migrate.")


main = MigrateApp.launch_instance
main = JupyterMigrate.launch_instance


if __name__ == '__main__':
Expand Down
4 changes: 2 additions & 2 deletions jupyter_core/tests/test_migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,14 @@ def test_migrate_custom_default(td):

def test_migrate_nothing(env):
migrate()
assert not os.path.exists(env['JUPYTER_CONFIG_DIR'])
assert os.listdir(env['JUPYTER_CONFIG_DIR']) == ['migrated']
assert not os.path.exists(env['JUPYTER_DATA_DIR'])


def test_migrate_default(env):
shutil.copytree(dotipython_empty, env['IPYTHONDIR'])
migrate()
assert not os.path.exists(env['JUPYTER_CONFIG_DIR'])
assert os.listdir(env['JUPYTER_CONFIG_DIR']) == ['migrated']
assert not os.path.exists(env['JUPYTER_DATA_DIR'])


Expand Down