def create_certificate(ip): ensure_dir_exists(CERT_DIRECTORY, mode=0o700) auth_cert_filepath, issuer, issuer_key = authority_certificate(ip) cert_filepath, key_filepath = server_certificate(ip, issuer, issuer_key) return auth_cert_filepath, cert_filepath, key_filepath
def test_migrate_config(td): profile = pjoin(td, 'profile') jpy = pjoin(td, 'jupyter_config') ensure_dir_exists(profile) env = { 'profile': profile, 'jupyter_config': jpy, } cfg_py = pjoin(profile, 'ipython_test_config.py') touch(cfg_py, 'c.Klass.trait = 5\n') empty_cfg_py = pjoin(profile, 'ipython_empty_config.py') touch(empty_cfg_py, '# c.Klass.trait = 5\n') assert not migrate_config('empty', env) assert not os.path.exists(jpy) with patch.dict(migrate_mod.config_substitutions, { re.compile(r'\bKlass\b'): 'Replaced', }): assert migrate_config('test', env) assert os.path.isdir(jpy) assert sorted(os.listdir(jpy)) == [ 'jupyter_test_config.py', ] with open(pjoin(jpy, 'jupyter_test_config.py')) as f: text = f.read() assert text == 'c.Replaced.trait = 5\n'
def write_default_config(self): """Write our default config to a .py config file""" if self.config_file: config_file = self.config_file else: config_file = os.path.join(self.config_dir, self.config_file_name + '.py') if os.path.exists(config_file) and not self.answer_yes: answer = '' def ask(): prompt = "Overwrite %s with default config? [y/N]" % config_file try: return raw_input(prompt).lower() or 'n' except KeyboardInterrupt: print('') # empty line return 'n' answer = ask() while not answer.startswith(('y', 'n')): print("Please answer 'yes' or 'no'") answer = ask() if answer.startswith('n'): return config_text = self.generate_config_file() if isinstance(config_text, bytes): config_text = config_text.decode('utf8') print("Writing default config to: %s" % config_file) ensure_dir_exists(os.path.abspath(os.path.dirname(config_file)), 0o700) with open(config_file, mode='w') as f: f.write(config_text)
def test_migrate_config(td): profile = pjoin(td, 'profile') jpy = pjoin(td, 'jupyter_config') ensure_dir_exists(profile) env = { 'profile': profile, 'jupyter_config': jpy, } cfg_py = pjoin(profile, 'ipython_test_config.py') touch(cfg_py, 'c.Klass.trait = 5\n') empty_cfg_py = pjoin(profile, 'ipython_empty_config.py') touch(empty_cfg_py, '# c.Klass.trait = 5\n') assert not migrate_config('empty', env) assert not os.path.exists(jpy) with patch.dict(migrate_mod.config_substitutions, { re.compile(r'\bKlass\b'): 'Replaced', }): assert migrate_config('test', env) assert os.path.isdir(jpy) assert sorted(os.listdir(jpy)) == [ 'jupyter_test_config.py', ] with open(pjoin(jpy, 'jupyter_test_config.py')) as f: text = f.read() assert text == 'c.Replaced.trait = 5\n'
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
def write_outputs(content, resources): """Step 3: Write the notebook to file This writes output from the exporter to file using the specified writer. It returns the results from the writer. Parameters ---------- output : resources : dict resources for a single notebook including name, config directory and directory to save output Returns ------- file results from the specified writer output of exporter """ # various paths and variables needed for the module notebook_namefull = resources['metadata']['name'] + resources.get( 'output_extension') outdir_nb = resources['metadata']['path'] outfile = os.path.join(outdir_nb, notebook_namefull) imgs_outdir = resources.get('output_files_dir') ensure_dir_exists(imgs_outdir) # write file in the appropriate format with io.open(outfile, 'w', encoding="utf-8") as fout: body = content.prettify(formatter='html') fout.write(body) # if the content has images then they are returned and saved if resources['outputs']: save_imgs(resources, imgs_outdir)
def setup_extensions(self, CUR_EXT, EXT_DIR, DIR): # Install the extensions to the required directories ensure_dir_exists(DIR) if os.path.exists(CUR_EXT): question = "ACTION: The Stepsize extension directory already " \ "exists, do you want to overwrite %s with the " \ "current installation?" % (CUR_EXT) prompt = self.query(question) if prompt == "yes": try: install_nbextension(EXT_DIR, overwrite=True, nbextensions_dir=DIR) print("OUTCOME: Added the extension to your " "%s directory" % (DIR)) except: print("WARNING: Unable to install the extension to your " "(nb)extensions folder") print("ERROR: %s" % (sys.exc_info()[0])) raise else: return else: try: install_nbextension(EXT_DIR, overwrite=True, nbextensions_dir=DIR) print("OUTCOME: Added the extension to your %s directory" % (DIR)) except: print("WARNING: Unable to install the extension to your " "(nb)extensions folder") print("ERROR: %s" % (sys.exc_info()[0])) raise
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
def setup_extensions(self, CUR_EXT, EXT_DIR, DIR): # Install the extensions to the required directories ensure_dir_exists(DIR) if os.path.exists(CUR_EXT): question = "ACTION: The Stepsize extension directory already " \ "exists, do you want to overwrite %s with the " \ "current installation?" % (CUR_EXT) prompt = self.query(question) if prompt == "yes": try: install_nbextension(EXT_DIR, overwrite=True, nbextensions_dir=DIR) print("OUTCOME: Added the extension to your " "%s directory" % (DIR)) except: print("WARNING: Unable to install the extension to your " "(nb)extensions folder") print("ERROR: %s" % (sys.exc_info()[0])) raise else: return else: try: install_nbextension(EXT_DIR, overwrite=True, nbextensions_dir=DIR) print("OUTCOME: Added the extension to your %s directory" % (DIR)) except: print("WARNING: Unable to install the extension to your " "(nb)extensions folder") print("ERROR: %s" % (sys.exc_info()[0])) raise
def write_default_config(self): """Write our default config to a .py config file""" if self.config_file: config_file = self.config_file else: config_file = os.path.join(self.config_dir, self.config_file_name + '.py') if os.path.exists(config_file) and not self.answer_yes: answer = '' def ask(): prompt = "Overwrite %s with default config? [y/N]" % config_file try: return input(prompt).lower() or 'n' except KeyboardInterrupt: print('') # empty line return 'n' answer = ask() while not answer.startswith(('y', 'n')): print("Please answer 'yes' or 'no'") answer = ask() if answer.startswith('n'): return config_text = self.generate_config_file() if isinstance(config_text, bytes): config_text = config_text.decode('utf8') print("Writing default config to: %s" % config_file) ensure_dir_exists(os.path.abspath(os.path.dirname(config_file)), 0o700) with open(config_file, mode='w') as f: f.write(config_text)
def get_filepaths(ip, dirname): directory = os.path.join(CERT_DIRECTORY, ip, dirname) ensure_dir_exists(directory, mode=0o700) cert_filepath = os.path.join(directory, CERT_FILENAME) key_filepath = os.path.join(directory, KEY_FILENAME) return cert_filepath, key_filepath
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_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_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 checkpoint_path(self, checkpoint_id, path): """find the path to a checkpoint""" path = path.strip('/') parent, name = ('/' + path).rsplit('/', 1) parent = parent.strip('/') basename, ext = os.path.splitext(name) filename = u"{name}-{checkpoint_id}{ext}".format( name=basename, checkpoint_id=checkpoint_id, ext=ext, ) os_path = self._get_os_path(path=parent) cp_dir = os.path.join(os_path, self.checkpoint_dir) with self.perm_to_403(): ensure_dir_exists(cp_dir) cp_path = os.path.join(cp_dir, filename) return cp_path
def init_connection_file(self): if not self.connection_file: self.connection_file = "kernel-%s.json" % os.getpid() try: self.connection_file = filefind(self.connection_file, [".", self.connection_dir]) except IOError: self.log.debug("Connection file not found: %s", self.connection_file) # This means I own it, and I'll create it in this directory: ensure_dir_exists(os.path.dirname(self.abs_connection_file), 0o700) # Also, I will clean it up: atexit.register(self.cleanup_connection_file) return try: self.load_connection_file() except Exception: self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True) self.exit(1)
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 init_connection_file(self): if not self.connection_file: self.connection_file = "kernel-%s.json"%os.getpid() try: self.connection_file = filefind(self.connection_file, ['.', self.connection_dir]) except IOError: self.log.debug("Connection file not found: %s", self.connection_file) # This means I own it, and I'll create it in this directory: ensure_dir_exists(os.path.dirname(self.abs_connection_file), 0o700) # Also, I will clean it up: atexit.register(self.cleanup_connection_file) return try: self.load_connection_file() except Exception: self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True) self.exit(1)
def setup_server_config(self, EXT_CONFIG): # Set the server extension to launch on startup by modifying the # jupyter_notebook_config.py CONFIG = os.path.join(jupyter_config_dir(), 'jupyter_config.py') ensure_dir_exists(jupyter_config_dir()) if os.path.isfile(CONFIG): pass else: c = JupyterApp() c.write_default_config() with open(CONFIG, 'r+') as fh: lines = fh.read() for i in EXT_CONFIG: if i not in lines: fh.seek(0, 2) fh.write('\n') fh.write(i) print("OUTCOME: Added the Stepsize server extension " "configuration to the jupyter_config.py")
def setup_server_config(self, EXT_CONFIG): # Set the server extension to launch on startup by modifying the # jupyter_notebook_config.py CONFIG = os.path.join(jupyter_config_dir(), 'jupyter_config.py') ensure_dir_exists(jupyter_config_dir()) if os.path.isfile(CONFIG): pass else: c = JupyterApp() c.write_default_config() with open(CONFIG, 'r+') as fh: lines = fh.read() for i in EXT_CONFIG: if i not in lines: fh.seek(0, 2) fh.write('\n') fh.write(i) print("OUTCOME: Added the Stepsize server extension " "configuration to the jupyter_config.py")
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 setup_notebook_config(self): # Set the notebook extension to launch on startup by # modifying the notebook.json ensure_dir_exists(ConfigManager()._config_dir_default()) # Elements to be added version = 2 data_element = {'stepsize_nb-ext/main': None} for i in range(1, version): data_element['stepsize_nb-ext/main_v0-%i' % i] = None data_element['stepsize_nb-ext/main_v0-%i' % version] = True data = {'load_extensions': data_element} try: cm = ConfigManager() cm.update('notebook', data) print("OUTCOME: Added the Stepsize notebook extension " "configuration to the notebook.json") except: print("WARNING: An error occured when trying to add %s to the " "notebook.json" % (data)) print("ERROR: %s" % (sys.exc_info()[0])) raise
def setup_notebook_config(self): # Set the notebook extension to launch on startup by # modifying the notebook.json ensure_dir_exists(ConfigManager()._config_dir_default()) # Elements to be added version = 2 data_element = {'stepsize_nb-ext/main': None} for i in range(1, version): data_element['stepsize_nb-ext/main_v0-%i' % i] = None data_element['stepsize_nb-ext/main_v0-%i' % version] = True data = {'load_extensions': data_element} try: cm = ConfigManager() cm.update('notebook', data) print("OUTCOME: Added the Stepsize notebook extension " "configuration to the notebook.json") except: print("WARNING: An error occured when trying to add %s to the " "notebook.json" % (data)) print("ERROR: %s" % (sys.exc_info()[0])) raise
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 _build_directory_changed(self, name, old, new): if new: ensure_dir_exists(new)
def install_nbextension(path, overwrite=False, symlink=False, user=False, prefix=None, nbextensions_dir=None, destination=None, verbose=DEPRECATED_ARGUMENT, logger=None, sys_prefix=False ): """Install a Javascript extension for the notebook Stages files and/or directories into the nbextensions directory. By default, this compares modification time, and only stages files that need updating. If `overwrite` is specified, matching files are purged before proceeding. Parameters ---------- path : path to file, directory, zip or tarball archive, or URL to install By default, the file will be installed with its base name, so '/path/to/foo' will install to 'nbextensions/foo'. See the destination argument below to change this. Archives (zip or tarballs) will be extracted into the nbextensions directory. overwrite : bool [default: False] If True, always install the files, regardless of what may already be installed. symlink : bool [default: False] If True, create a symlink in nbextensions, rather than copying files. Not allowed with URLs or archives. Windows support for symlinks requires Vista or above, Python 3, and a permission bit which only admin users have by default, so don't rely on it. user : bool [default: False] Whether to install to the user's nbextensions directory. Otherwise do a system-wide install (e.g. /usr/local/share/jupyter/nbextensions). prefix : str [optional] Specify install prefix, if it should differ from default (e.g. /usr/local). Will install to ``<prefix>/share/jupyter/nbextensions`` nbextensions_dir : str [optional] Specify absolute path of nbextensions directory explicitly. destination : str [optional] name the nbextension is installed to. For example, if destination is 'foo', then the source file will be installed to 'nbextensions/foo', regardless of the source name. This cannot be specified if an archive is given as the source. logger : Jupyter logger [optional] Logger instance to use """ if verbose != DEPRECATED_ARGUMENT: import warnings warnings.warn("`install_nbextension`'s `verbose` parameter is deprecated, it will have no effects and will be removed in Notebook 5.0", DeprecationWarning) # the actual path to which we eventually installed full_dest = None nbext = _get_nbextension_dir(user=user, sys_prefix=sys_prefix, prefix=prefix, nbextensions_dir=nbextensions_dir) # make sure nbextensions dir exists ensure_dir_exists(nbext) # forcing symlink parameter to False if os.symlink does not exist (e.g., on Windows machines running python 2) if not hasattr(os, 'symlink'): symlink = False if isinstance(path, (list, tuple)): raise TypeError("path must be a string pointing to a single extension to install; call this function multiple times to install multiple extensions") path = cast_unicode_py2(path) if path.startswith(('https://', 'http://')): if symlink: raise ValueError("Cannot symlink from URLs") # Given a URL, download it with TemporaryDirectory() as td: filename = urlparse(path).path.split('/')[-1] local_path = os.path.join(td, filename) if logger: logger.info("Downloading: %s -> %s" % (path, local_path)) urlretrieve(path, local_path) # now install from the local copy full_dest = install_nbextension(local_path, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, destination=destination, logger=logger) elif path.endswith('.zip') or _safe_is_tarfile(path): if symlink: raise ValueError("Cannot symlink from archives") if destination: raise ValueError("Cannot give destination for archives") if logger: logger.info("Extracting: %s -> %s" % (path, nbext)) if path.endswith('.zip'): archive = zipfile.ZipFile(path) elif _safe_is_tarfile(path): archive = tarfile.open(path) archive.extractall(nbext) archive.close() # TODO: what to do here full_dest = None else: if not destination: destination = basename(path) destination = cast_unicode_py2(destination) full_dest = normpath(pjoin(nbext, destination)) if overwrite and os.path.lexists(full_dest): if logger: logger.info("Removing: %s" % full_dest) if os.path.isdir(full_dest) and not os.path.islink(full_dest): shutil.rmtree(full_dest) else: os.remove(full_dest) if symlink: path = os.path.abspath(path) if not os.path.exists(full_dest): if logger: logger.info("Symlinking: %s -> %s" % (full_dest, path)) os.symlink(path, full_dest) elif os.path.isdir(path): path = pjoin(os.path.abspath(path), '') # end in path separator for parent, dirs, files in os.walk(path): dest_dir = pjoin(full_dest, parent[len(path):]) if not os.path.exists(dest_dir): if logger: logger.info("Making directory: %s" % dest_dir) os.makedirs(dest_dir) for file in files: src = pjoin(parent, file) dest_file = pjoin(dest_dir, file) _maybe_copy(src, dest_file, logger=logger) else: src = path _maybe_copy(src, full_dest, logger=logger) return full_dest
def _get_image_tag(self, match, path = None, format = "png"): """ Return (X)HTML mark-up for the image-tag given by match. Parameters ---------- match : re.SRE_Match A match to an HTML image tag as exported by Qt, with match.group("Name") containing the matched image ID. path : string|None, optional [default None] If not None, specifies a path to which supporting files may be written (e.g., for linked images). If None, all images are to be included inline. format : "png"|"svg"|"jpg", optional [default "png"] Format for returned or referenced images. """ if format in ("png","jpg"): try: image = self._get_image(match.group("name")) except KeyError: return "<b>Couldn't find image %s</b>" % match.group("name") if path is not None: ensure_dir_exists(path) relpath = os.path.basename(path) if image.save("%s/qt_img%s.%s" % (path, match.group("name"), format), "PNG"): return '<img src="%s/qt_img%s.%s">' % (relpath, match.group("name"),format) else: return "<b>Couldn't save image!</b>" else: ba = QtCore.QByteArray() buffer_ = QtCore.QBuffer(ba) buffer_.open(QtCore.QIODevice.WriteOnly) image.save(buffer_, format.upper()) buffer_.close() return '<img src="data:image/%s;base64,\n%s\n" />' % ( format,re.sub(r'(.{60})',r'\1\n',str(ba.toBase64()))) elif format == "svg": try: svg = str(self._name_to_svg_map[match.group("name")]) except KeyError: if not self._svg_warning_displayed: QtGui.QMessageBox.warning(self, 'Error converting PNG to SVG.', 'Cannot convert PNG images to SVG, export with PNG figures instead. ' 'If you want to export matplotlib figures as SVG, add ' 'to your ipython config:\n\n' '\tc.InlineBackend.figure_format = \'svg\'\n\n' 'And regenerate the figures.', QtGui.QMessageBox.Ok) self._svg_warning_displayed = True return ("<b>Cannot convert PNG images to SVG.</b> " "You must export this session with PNG images. " "If you want to export matplotlib figures as SVG, add to your config " "<span>c.InlineBackend.figure_format = 'svg'</span> " "and regenerate the figures.") # Not currently checking path, because it's tricky to find a # cross-browser way to embed external SVG images (e.g., via # object or embed tags). # Chop stand-alone header from matplotlib SVG offset = svg.find("<svg") assert(offset > -1) return svg[offset:] else: return '<b>Unrecognized image format</b>'
def touch(path, content=''): ensure_dir_exists(os.path.dirname(path)) with open(path, 'w') as f: f.write(content)
def _get_image_tag(self, match, path=None, format="png"): """ Return (X)HTML mark-up for the image-tag given by match. Parameters ---------- match : re.SRE_Match A match to an HTML image tag as exported by Qt, with match.group("Name") containing the matched image ID. path : string|None, optional [default None] If not None, specifies a path to which supporting files may be written (e.g., for linked images). If None, all images are to be included inline. format : "png"|"svg"|"jpg", optional [default "png"] Format for returned or referenced images. """ if format in ("png", "jpg"): try: image = self._get_image(match.group("name")) except KeyError: return "<b>Couldn't find image %s</b>" % match.group("name") if path is not None: ensure_dir_exists(path) relpath = os.path.basename(path) if image.save( "%s/qt_img%s.%s" % (path, match.group("name"), format), "PNG"): return '<img src="%s/qt_img%s.%s">' % ( relpath, match.group("name"), format) else: return "<b>Couldn't save image!</b>" else: ba = QtCore.QByteArray() buffer_ = QtCore.QBuffer(ba) buffer_.open(QtCore.QIODevice.WriteOnly) image.save(buffer_, format.upper()) buffer_.close() return '<img src="data:image/%s;base64,\n%s\n" />' % ( format, re.sub(r'(.{60})', r'\1\n', str(ba.toBase64().data().decode()))) elif format == "svg": try: svg = str(self._name_to_svg_map[match.group("name")]) except KeyError: if not self._svg_warning_displayed: QtWidgets.QMessageBox.warning( self, 'Error converting PNG to SVG.', 'Cannot convert PNG images to SVG, export with PNG figures instead. ' 'If you want to export matplotlib figures as SVG, add ' 'to your ipython config:\n\n' '\tc.InlineBackend.figure_format = \'svg\'\n\n' 'And regenerate the figures.', QtWidgets.QMessageBox.Ok) self._svg_warning_displayed = True return ( "<b>Cannot convert PNG images to SVG.</b> " "You must export this session with PNG images. " "If you want to export matplotlib figures as SVG, add to your config " "<span>c.InlineBackend.figure_format = 'svg'</span> " "and regenerate the figures.") # Not currently checking path, because it's tricky to find a # cross-browser way to embed external SVG images (e.g., via # object or embed tags). # Chop stand-alone header from matplotlib SVG offset = svg.find("<svg") assert (offset > -1) return svg[offset:] else: return '<b>Unrecognized image format</b>'
def install_labextension(path, name, overwrite=False, symlink=False, user=False, prefix=None, labextensions_dir=None, logger=None, sys_prefix=False ): """Install a Javascript extension for JupyterLab Stages files and/or directories into the labextensions directory. By default, this compares modification time, and only stages files that need updating. If `overwrite` is specified, matching files are purged before proceeding. Parameters ---------- path : path to file, directory, zip or tarball archive, or URL to install Archives (zip or tarballs) will be extracted into the labextensions directory. name : str name the labextension is installed to. For example, if name is 'foo', then the source file will be installed to 'labextensions/foo'. overwrite : bool [default: False] If True, always install the files, regardless of what may already be installed. symlink : bool [default: False] If True, create a symlink in labextensions, rather than copying files. Not allowed with URLs or archives. Windows support for symlinks requires Vista or above, Python 3, and a permission bit which only admin users have by default, so don't rely on it. user : bool [default: False] Whether to install to the user's labextensions directory. Otherwise do a system-wide install (e.g. /usr/local/share/jupyter/labextensions). prefix : str [optional] Specify install prefix, if it should differ from default (e.g. /usr/local). Will install to ``<prefix>/share/jupyter/labextensions`` labextensions_dir : str [optional] Specify absolute path of labextensions directory explicitly. logger : Jupyter logger [optional] Logger instance to use sys_prefix : bool [default: False] Install into the sys.prefix, i.e. environment """ # the actual path to which we eventually installed full_dest = None labext = _get_labextension_dir(user=user, sys_prefix=sys_prefix, prefix=prefix, labextensions_dir=labextensions_dir) # make sure labextensions dir exists ensure_dir_exists(labext) # forcing symlink parameter to False if os.symlink does not exist (e.g., on Windows machines running python 2) if not hasattr(os, 'symlink'): symlink = False if isinstance(path, (list, tuple)): raise TypeError("path must be a string pointing to a single extension to install; call this function multiple times to install multiple extensions") path = cast_unicode_py2(path) if path.startswith(('https://', 'http://')): raise NotImplementedError('Urls are not yet supported for labextensions') elif path.endswith('.zip') or _safe_is_tarfile(path): raise NotImplementedError('Archive files are not yet supported for labextensions') else: destination = cast_unicode_py2(name) full_dest = normpath(pjoin(labext, destination)) if overwrite and os.path.lexists(full_dest): if logger: logger.info("Removing: %s" % full_dest) if os.path.isdir(full_dest) and not os.path.islink(full_dest): shutil.rmtree(full_dest) else: os.remove(full_dest) if symlink: path = os.path.abspath(path) if not os.path.exists(full_dest): if logger: logger.info("Symlinking: %s -> %s" % (full_dest, path)) os.symlink(path, full_dest) elif os.path.isdir(path): path = pjoin(os.path.abspath(path), '') # end in path separator for parent, dirs, files in os.walk(path): dest_dir = pjoin(full_dest, parent[len(path):]) if not os.path.exists(dest_dir): if logger: logger.info("Making directory: %s" % dest_dir) os.makedirs(dest_dir) for file in files: src = pjoin(parent, file) dest_file = pjoin(dest_dir, file) _maybe_copy(src, dest_file, logger=logger) else: src = path _maybe_copy(src, full_dest, logger=logger) return full_dest
def _runtime_dir_default(self): rd = jupyter_runtime_dir() ensure_dir_exists(rd, mode=0o700) return rd
def _build_directory_changed(self, change): new = change['new'] if new: ensure_dir_exists(new)
def _connection_dir_default(self): d = jupyter_runtime_dir() ensure_dir_exists(d, 0o700) return d
def _makedir(self, path): """Make a directory if it doesn't already exist""" if path: self.log.info("Making directory %s", path) ensure_dir_exists(path)
def touch(path, content=''): ensure_dir_exists(os.path.dirname(path)) with open(path, 'w') as f: f.write(content)
def _makedir(self, path): """Make a directory if it doesn't already exist""" if path: self.log.info("Making directory %s", path) ensure_dir_exists(path)
def _build_directory_changed(self, change): new = change['new'] if new: ensure_dir_exists(new)
def _runtime_dir_default(self): rd = jupyter_runtime_dir() ensure_dir_exists(rd, mode=0o700) return rd
def install_nbextension(path, overwrite=False, symlink=False, user=False, prefix=None, nbextensions_dir=None, destination=None, verbose=DEPRECATED_ARGUMENT, logger=None, sys_prefix=False): """Install a Javascript extension for the notebook Stages files and/or directories into the nbextensions directory. By default, this compares modification time, and only stages files that need updating. If `overwrite` is specified, matching files are purged before proceeding. Parameters ---------- path : path to file, directory, zip or tarball archive, or URL to install By default, the file will be installed with its base name, so '/path/to/foo' will install to 'nbextensions/foo'. See the destination argument below to change this. Archives (zip or tarballs) will be extracted into the nbextensions directory. overwrite : bool [default: False] If True, always install the files, regardless of what may already be installed. symlink : bool [default: False] If True, create a symlink in nbextensions, rather than copying files. Not allowed with URLs or archives. Windows support for symlinks requires Vista or above, Python 3, and a permission bit which only admin users have by default, so don't rely on it. user : bool [default: False] Whether to install to the user's nbextensions directory. Otherwise do a system-wide install (e.g. /usr/local/share/jupyter/nbextensions). prefix : str [optional] Specify install prefix, if it should differ from default (e.g. /usr/local). Will install to ``<prefix>/share/jupyter/nbextensions`` nbextensions_dir : str [optional] Specify absolute path of nbextensions directory explicitly. destination : str [optional] name the nbextension is installed to. For example, if destination is 'foo', then the source file will be installed to 'nbextensions/foo', regardless of the source name. This cannot be specified if an archive is given as the source. logger : Jupyter logger [optional] Logger instance to use """ if verbose != DEPRECATED_ARGUMENT: import warnings warnings.warn( "`install_nbextension`'s `verbose` parameter is deprecated, it will have no effects and will be removed in Notebook 5.0", DeprecationWarning) # the actual path to which we eventually installed full_dest = None nbext = _get_nbextension_dir(user=user, sys_prefix=sys_prefix, prefix=prefix, nbextensions_dir=nbextensions_dir) # make sure nbextensions dir exists ensure_dir_exists(nbext) # forcing symlink parameter to False if os.symlink does not exist (e.g., on Windows machines running python 2) if not hasattr(os, 'symlink'): symlink = False if isinstance(path, (list, tuple)): raise TypeError( "path must be a string pointing to a single extension to install; call this function multiple times to install multiple extensions" ) path = cast_unicode_py2(path) if path.startswith(('https://', 'http://')): if symlink: raise ValueError("Cannot symlink from URLs") # Given a URL, download it with TemporaryDirectory() as td: filename = urlparse(path).path.split('/')[-1] local_path = os.path.join(td, filename) if logger: logger.info("Downloading: %s -> %s" % (path, local_path)) urlretrieve(path, local_path) # now install from the local copy full_dest = install_nbextension(local_path, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, destination=destination, logger=logger) elif path.endswith('.zip') or _safe_is_tarfile(path): if symlink: raise ValueError("Cannot symlink from archives") if destination: raise ValueError("Cannot give destination for archives") if logger: logger.info("Extracting: %s -> %s" % (path, nbext)) if path.endswith('.zip'): archive = zipfile.ZipFile(path) elif _safe_is_tarfile(path): archive = tarfile.open(path) archive.extractall(nbext) archive.close() # TODO: what to do here full_dest = None else: if not destination: destination = basename(normpath(path)) destination = cast_unicode_py2(destination) full_dest = normpath(pjoin(nbext, destination)) if overwrite and os.path.lexists(full_dest): if logger: logger.info("Removing: %s" % full_dest) if os.path.isdir(full_dest) and not os.path.islink(full_dest): shutil.rmtree(full_dest) else: os.remove(full_dest) if symlink: path = os.path.abspath(path) if not os.path.exists(full_dest): if logger: logger.info("Symlinking: %s -> %s" % (full_dest, path)) os.symlink(path, full_dest) elif os.path.isdir(path): path = pjoin(os.path.abspath(path), '') # end in path separator for parent, dirs, files in os.walk(path): dest_dir = pjoin(full_dest, parent[len(path):]) if not os.path.exists(dest_dir): if logger: logger.info("Making directory: %s" % dest_dir) os.makedirs(dest_dir) for file_name in files: src = pjoin(parent, file_name) dest_file = pjoin(dest_dir, file_name) _maybe_copy(src, dest_file, logger=logger) else: src = path _maybe_copy(src, full_dest, logger=logger) return full_dest
def _runtime_dir_changed(self, new): ensure_dir_exists(new, mode=0o700)
def install_labextension(path, name, overwrite=False, symlink=False, user=False, prefix=None, labextensions_dir=None, logger=None, sys_prefix=False): """Install a Javascript extension for JupyterLab Stages files and/or directories into the labextensions directory. By default, this compares modification time, and only stages files that need updating. If `overwrite` is specified, matching files are purged before proceeding. Parameters ---------- path : path to file, directory, zip or tarball archive, or URL to install Archives (zip or tarballs) will be extracted into the labextensions directory. name : str name the labextension is installed to. For example, if name is 'foo', then the source file will be installed to 'labextensions/foo'. overwrite : bool [default: False] If True, always install the files, regardless of what may already be installed. symlink : bool [default: False] If True, create a symlink in labextensions, rather than copying files. Not allowed with URLs or archives. Windows support for symlinks requires Vista or above, Python 3, and a permission bit which only admin users have by default, so don't rely on it. user : bool [default: False] Whether to install to the user's labextensions directory. Otherwise do a system-wide install (e.g. /usr/local/share/jupyter/labextensions). prefix : str [optional] Specify install prefix, if it should differ from default (e.g. /usr/local). Will install to ``<prefix>/share/jupyter/labextensions`` labextensions_dir : str [optional] Specify absolute path of labextensions directory explicitly. logger : Jupyter logger [optional] Logger instance to use sys_prefix : bool [default: False] Install into the sys.prefix, i.e. environment """ # the actual path to which we eventually installed full_dest = None labext = _get_labextension_dir(user=user, sys_prefix=sys_prefix, prefix=prefix, labextensions_dir=labextensions_dir) # make sure labextensions dir exists ensure_dir_exists(labext) # forcing symlink parameter to False if os.symlink does not exist (e.g., on Windows machines running python 2) if not hasattr(os, 'symlink'): symlink = False if isinstance(path, (list, tuple)): raise TypeError( "path must be a string pointing to a single extension to install; call this function multiple times to install multiple extensions" ) path = cast_unicode_py2(path) if path.startswith(('https://', 'http://')): raise NotImplementedError( 'Urls are not yet supported for labextensions') elif path.endswith('.zip') or _safe_is_tarfile(path): raise NotImplementedError( 'Archive files are not yet supported for labextensions') else: destination = cast_unicode_py2(name) full_dest = normpath(pjoin(labext, destination)) if overwrite and os.path.lexists(full_dest): if logger: logger.info("Removing: %s" % full_dest) if os.path.isdir(full_dest) and not os.path.islink(full_dest): shutil.rmtree(full_dest) else: os.remove(full_dest) if symlink: path = os.path.abspath(path) if not os.path.exists(full_dest): if logger: logger.info("Symlinking: %s -> %s" % (full_dest, path)) os.symlink(path, full_dest) elif os.path.isdir(path): path = pjoin(os.path.abspath(path), '') # end in path separator for parent, dirs, files in os.walk(path): dest_dir = pjoin(full_dest, parent[len(path):]) if not os.path.exists(dest_dir): if logger: logger.info("Making directory: %s" % dest_dir) os.makedirs(dest_dir) for file in files: src = pjoin(parent, file) dest_file = pjoin(dest_dir, file) _maybe_copy(src, dest_file, logger=logger) else: src = path _maybe_copy(src, full_dest, logger=logger) return full_dest
def _data_dir_default(self): d = jupyter_data_dir() ensure_dir_exists(d, mode=0o700) return d
def _runtime_dir_changed(self, new): ensure_dir_exists(new, mode=0o700)
def install_nbextension(path, overwrite=False, symlink=False, user=False, prefix=None, nbextensions_dir=None, destination=None, verbose=1): """Install a Javascript extension for the notebook Stages files and/or directories into the nbextensions directory. By default, this compares modification time, and only stages files that need updating. If `overwrite` is specified, matching files are purged before proceeding. Parameters ---------- path : path to file, directory, zip or tarball archive, or URL to install By default, the file will be installed with its base name, so '/path/to/foo' will install to 'nbextensions/foo'. See the destination argument below to change this. Archives (zip or tarballs) will be extracted into the nbextensions directory. overwrite : bool [default: False] If True, always install the files, regardless of what may already be installed. symlink : bool [default: False] If True, create a symlink in nbextensions, rather than copying files. Not allowed with URLs or archives. Windows support for symlinks requires Vista or above, Python 3, and a permission bit which only admin users have by default, so don't rely on it. user : bool [default: False] Whether to install to the user's nbextensions directory. Otherwise do a system-wide install (e.g. /usr/local/share/jupyter/nbextensions). prefix : str [optional] Specify install prefix, if it should differ from default (e.g. /usr/local). Will install to ``<prefix>/share/jupyter/nbextensions`` nbextensions_dir : str [optional] Specify absolute path of nbextensions directory explicitly. destination : str [optional] name the nbextension is installed to. For example, if destination is 'foo', then the source file will be installed to 'nbextensions/foo', regardless of the source name. This cannot be specified if an archive is given as the source. verbose : int [default: 1] Set verbosity level. The default is 1, where file actions are printed. set verbose=2 for more output, or verbose=0 for silence. """ nbext = _get_nbext_dir(nbextensions_dir, user, prefix) # make sure nbextensions dir exists ensure_dir_exists(nbext) if isinstance(path, (list, tuple)): raise TypeError( "path must be a string pointing to a single extension to install; call this function multiple times to install multiple extensions" ) path = cast_unicode_py2(path) if path.startswith(('https://', 'http://')): if symlink: raise ValueError("Cannot symlink from URLs") # Given a URL, download it with TemporaryDirectory() as td: filename = urlparse(path).path.split('/')[-1] local_path = os.path.join(td, filename) if verbose >= 1: print("downloading %s to %s" % (path, local_path)) urlretrieve(path, local_path) # now install from the local copy install_nbextension(local_path, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, destination=destination, verbose=verbose) elif path.endswith('.zip') or _safe_is_tarfile(path): if symlink: raise ValueError("Cannot symlink from archives") if destination: raise ValueError("Cannot give destination for archives") if verbose >= 1: print("extracting %s to %s" % (path, nbext)) if path.endswith('.zip'): archive = zipfile.ZipFile(path) elif _safe_is_tarfile(path): archive = tarfile.open(path) archive.extractall(nbext) archive.close() else: if not destination: destination = basename(path) destination = cast_unicode_py2(destination) full_dest = pjoin(nbext, destination) if overwrite and os.path.lexists(full_dest): if verbose >= 1: print("removing %s" % full_dest) if os.path.isdir(full_dest) and not os.path.islink(full_dest): shutil.rmtree(full_dest) else: os.remove(full_dest) if symlink: path = os.path.abspath(path) if not os.path.exists(full_dest): if verbose >= 1: print("symlink %s -> %s" % (full_dest, path)) os.symlink(path, full_dest) elif os.path.isdir(path): path = pjoin(os.path.abspath(path), '') # end in path separator for parent, dirs, files in os.walk(path): dest_dir = pjoin(full_dest, parent[len(path):]) if not os.path.exists(dest_dir): if verbose >= 2: print("making directory %s" % dest_dir) os.makedirs(dest_dir) for file in files: src = pjoin(parent, file) # print("%r, %r" % (dest_dir, file)) dest_file = pjoin(dest_dir, file) _maybe_copy(src, dest_file, verbose) else: src = path _maybe_copy(src, full_dest, verbose)
def _data_dir_default(self): d = jupyter_data_dir() ensure_dir_exists(d, mode=0o700) return d
def _connection_dir_default(self): d = jupyter_runtime_dir() ensure_dir_exists(d, 0o700) return d
def install_nbextension(path, overwrite=False, symlink=False, user=False, prefix=None, nbextensions_dir=None, destination=None, verbose=1): """Install a Javascript extension for the notebook Stages files and/or directories into the nbextensions directory. By default, this compares modification time, and only stages files that need updating. If `overwrite` is specified, matching files are purged before proceeding. Parameters ---------- path : path to file, directory, zip or tarball archive, or URL to install By default, the file will be installed with its base name, so '/path/to/foo' will install to 'nbextensions/foo'. See the destination argument below to change this. Archives (zip or tarballs) will be extracted into the nbextensions directory. overwrite : bool [default: False] If True, always install the files, regardless of what may already be installed. symlink : bool [default: False] If True, create a symlink in nbextensions, rather than copying files. Not allowed with URLs or archives. Windows support for symlinks requires Vista or above, Python 3, and a permission bit which only admin users have by default, so don't rely on it. user : bool [default: False] Whether to install to the user's nbextensions directory. Otherwise do a system-wide install (e.g. /usr/local/share/jupyter/nbextensions). prefix : str [optional] Specify install prefix, if it should differ from default (e.g. /usr/local). Will install to ``<prefix>/share/jupyter/nbextensions`` nbextensions_dir : str [optional] Specify absolute path of nbextensions directory explicitly. destination : str [optional] name the nbextension is installed to. For example, if destination is 'foo', then the source file will be installed to 'nbextensions/foo', regardless of the source name. This cannot be specified if an archive is given as the source. verbose : int [default: 1] Set verbosity level. The default is 1, where file actions are printed. set verbose=2 for more output, or verbose=0 for silence. """ nbext = _get_nbext_dir(nbextensions_dir, user, prefix) # make sure nbextensions dir exists ensure_dir_exists(nbext) if isinstance(path, (list, tuple)): raise TypeError("path must be a string pointing to a single extension to install; call this function multiple times to install multiple extensions") path = cast_unicode_py2(path) if path.startswith(('https://', 'http://')): if symlink: raise ValueError("Cannot symlink from URLs") # Given a URL, download it with TemporaryDirectory() as td: filename = urlparse(path).path.split('/')[-1] local_path = os.path.join(td, filename) if verbose >= 1: print("downloading %s to %s" % (path, local_path)) urlretrieve(path, local_path) # now install from the local copy install_nbextension(local_path, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, destination=destination, verbose=verbose) elif path.endswith('.zip') or _safe_is_tarfile(path): if symlink: raise ValueError("Cannot symlink from archives") if destination: raise ValueError("Cannot give destination for archives") if verbose >= 1: print("extracting %s to %s" % (path, nbext)) if path.endswith('.zip'): archive = zipfile.ZipFile(path) elif _safe_is_tarfile(path): archive = tarfile.open(path) archive.extractall(nbext) archive.close() else: if not destination: destination = basename(path) destination = cast_unicode_py2(destination) full_dest = pjoin(nbext, destination) if overwrite and os.path.lexists(full_dest): if verbose >= 1: print("removing %s" % full_dest) if os.path.isdir(full_dest) and not os.path.islink(full_dest): shutil.rmtree(full_dest) else: os.remove(full_dest) if symlink: path = os.path.abspath(path) if not os.path.exists(full_dest): if verbose >= 1: print("symlink %s -> %s" % (full_dest, path)) os.symlink(path, full_dest) elif os.path.isdir(path): path = pjoin(os.path.abspath(path), '') # end in path separator for parent, dirs, files in os.walk(path): dest_dir = pjoin(full_dest, parent[len(path):]) if not os.path.exists(dest_dir): if verbose >= 2: print("making directory %s" % dest_dir) os.makedirs(dest_dir) for file in files: src = pjoin(parent, file) # print("%r, %r" % (dest_dir, file)) dest_file = pjoin(dest_dir, file) _maybe_copy(src, dest_file, verbose) else: src = path _maybe_copy(src, full_dest, verbose)
def install_nbextension(path, overwrite=False, symlink=False, user=False, prefix=None, nbextensions_dir=None, destination=None, logger=None, sys_prefix=False): """Install a Javascript extension for the notebook.""" # the actual path to which we eventually installed full_dest = None nbext = _get_nbextension_dir( user=user, sys_prefix=sys_prefix, prefix=prefix, nbextensions_dir=nbextensions_dir) # make sure nbextensions dir exists ensure_dir_exists(nbext) # forcing symlink parameter to False if os.symlink does not exist (e.g., # on Windows machines running python 2) if not hasattr(os, 'symlink'): symlink = False if isinstance(path, (list, tuple)): raise TypeError("path must be a string pointing to a single extension " "to install; call this function multiple times to " "install multiple extensions") path = cast_unicode_py2(path) if path.startswith(('https://', 'http://')): if symlink: raise ValueError("Cannot symlink from URLs") # Given a URL, download it with TemporaryDirectory() as td: filename = urlparse(path).path.split('/')[-1] local_path = os.path.join(td, filename) if logger: logger.info("Downloading: %s -> %s" % (path, local_path)) urlretrieve(path, local_path) # now install from the local copy full_dest = install_nbextension( local_path, overwrite=overwrite, symlink=symlink, nbextensions_dir=nbext, destination=destination, logger=logger) elif path.endswith('.zip') or _safe_is_tarfile(path): if symlink: raise ValueError("Cannot symlink from archives") if destination: raise ValueError("Cannot give destination for archives") if logger: logger.info("Extracting: %s -> %s" % (path, nbext)) if path.endswith('.zip'): archive = zipfile.ZipFile(path) elif _safe_is_tarfile(path): archive = tarfile.open(path) archive.extractall(nbext) archive.close() # TODO: what to do here full_dest = None else: if not destination: destination = os.path.basename(path) destination = cast_unicode_py2(destination) full_dest = os.path.normpath(os.path.join(nbext, destination)) if overwrite and os.path.lexists(full_dest): if logger: logger.info("Removing: %s" % full_dest) if os.path.isdir(full_dest) and not os.path.islink(full_dest): shutil.rmtree(full_dest) else: os.remove(full_dest) if symlink: path = os.path.abspath(path) if not os.path.exists(full_dest): if logger: logger.info("Symlinking: %s -> %s" % (full_dest, path)) os.symlink(path, full_dest) elif os.path.isdir(path): # end in path separator path = os.path.join(os.path.abspath(path), '') for parent, dirs, files in os.walk(path): dest_dir = os.path.join(full_dest, parent[len(path):]) if not os.path.exists(dest_dir): if logger: logger.info("Making directory: %s" % dest_dir) os.makedirs(dest_dir) for file in files: src = os.path.join(parent, file) dest_file = os.path.join(dest_dir, file) _maybe_copy(src, dest_file, logger=logger) else: src = path _maybe_copy(src, full_dest, logger=logger) return full_dest