def new_author(name=None, email=None, affiliation=None, url=None): """Create a new author.""" author = NotebookNode() if name is not None: author.name = cast_unicode(name) if email is not None: author.email = cast_unicode(email) if affiliation is not None: author.affiliation = cast_unicode(affiliation) if url is not None: author.url = cast_unicode(url) return author
def persist_config(config_file=None, mode=0o600): """Context manager that can be used to modify a config object On exit of the context manager, the config will be written back to disk, by default with user-only (600) permissions. """ if config_file is None: config_file = os.path.join(jupyter_config_dir(), 'jupyter_notebook_config.json') loader = JSONFileConfigLoader(os.path.basename(config_file), os.path.dirname(config_file)) try: config = loader.load_config() except ConfigFileNotFound: config = Config() yield config with io.open(config_file, 'w', encoding='utf8') as f: f.write(cast_unicode(json.dumps(config, indent=2))) try: os.chmod(config_file, mode) except Exception as e: tb = traceback.format_exc() warnings.warn("Failed to set permissions on %s:\n%s" % (config_file, tb), RuntimeWarning)
def get(self, kernel_id): self.kernel_id = cast_unicode(kernel_id, 'ascii') print("kernelid+ " + self.kernel_id) if options.kernel_lock.get(self.kernel_id) is None: lock = threading.Lock() options.kernel_lock[kernel_id] = lock yield super(ZMQChannelsHandler, self).get(kernel_id=kernel_id)
def get(self, kernel_id, *args, **kwargs): self.authenticate() self.kernel_id = cast_unicode(kernel_id, 'ascii') yield gen.maybe_future( super(WebSocketChannelsHandler, self).get(kernel_id=kernel_id, *args, **kwargs))
def complete_request(self, text): line = str_to_unicode(readline.get_line_buffer()) byte_cursor_pos = readline.get_endidx() # get_endidx is a byte offset # account for multi-byte characters to get correct cursor_pos bytes_before_cursor = cast_bytes(line)[:byte_cursor_pos] cursor_pos = len(cast_unicode(bytes_before_cursor)) # send completion request to kernel # Give the kernel up to 5s to respond msg_id = self.client.complete( code=line, cursor_pos=cursor_pos, ) msg = self.client.shell_channel.get_msg(timeout=self.timeout) if msg['parent_header']['msg_id'] == msg_id: content = msg['content'] cursor_start = content['cursor_start'] matches = [line[:cursor_start] + m for m in content['matches']] if content["cursor_end"] < cursor_pos: extra = line[content["cursor_end"]:cursor_pos] matches = [m + extra for m in matches] matches = [unicode_to_str(m) for m in matches] return matches return []
def is_file_hidden_win(abs_path, stat_res=None): """Is a file hidden? This only checks the file itself; it should be called in combination with checking the directory containing the file. Use is_hidden() instead to check the file and its parent directories. Parameters ---------- abs_path : unicode The absolute path to check. stat_res : os.stat_result, optional Ignored on Windows, exists for compatibility with POSIX version of the function. """ if os.path.basename(abs_path).startswith('.'): return True win32_FILE_ATTRIBUTE_HIDDEN = 0x02 try: attrs = ctypes.windll.kernel32.GetFileAttributesW( py3compat.cast_unicode(abs_path)) except AttributeError: pass else: if attrs > 0 and attrs & win32_FILE_ATTRIBUTE_HIDDEN: return True return False
def _reserialize_reply(self, msg_or_list, channel=None): """Reserialize a reply message using JSON. msg_or_list can be an already-deserialized msg dict or the zmq buffer list. If it is the zmq list, it will be deserialized with self.session. This takes the msg list from the ZMQ socket and serializes the result for the websocket. This method should be used by self._on_zmq_reply to build messages that can be sent back to the browser. """ if isinstance(msg_or_list, dict): # already unpacked msg = msg_or_list else: idents, msg_list = self.session.feed_identities(msg_or_list) msg = self.session.deserialize(msg_list) if channel: msg['channel'] = channel if msg['buffers']: buf = serialize_binary_message(msg) return buf else: smsg = json.dumps(msg, default=date_default) return cast_unicode(smsg)
def complete_request(self, text): line = str_to_unicode(readline.get_line_buffer()) byte_cursor_pos = readline.get_endidx() # get_endidx is a byte offset # account for multi-byte characters to get correct cursor_pos bytes_before_cursor = cast_bytes(line)[:byte_cursor_pos] cursor_pos = len(cast_unicode(bytes_before_cursor)) # send completion request to kernel # Give the kernel up to 5s to respond msg_id = self.client.complete( code=line, cursor_pos=cursor_pos, ) msg = self.client.shell_channel.get_msg(timeout=self.timeout) if msg['parent_header']['msg_id'] == msg_id: content = msg['content'] cursor_start = content['cursor_start'] matches = [ line[:cursor_start] + m for m in content['matches'] ] if content["cursor_end"] < cursor_pos: extra = line[content["cursor_end"]: cursor_pos] matches = [m + extra for m in matches] matches = [ unicode_to_str(m) for m in matches ] return matches return []
def persist_config(config_file=None, mode=0o600): """Context manager that can be used to modify a config object On exit of the context manager, the config will be written back to disk, by default with user-only (600) permissions. """ if config_file is None: config_file = os.path.join(jupyter_config_dir(), 'jupyter_notebook_config.json') os.makedirs(os.path.dirname(config_file), exist_ok=True) loader = JSONFileConfigLoader(os.path.basename(config_file), os.path.dirname(config_file)) try: config = loader.load_config() except ConfigFileNotFound: config = Config() yield config with io.open(config_file, 'w', encoding='utf8') as f: f.write(cast_unicode(json.dumps(config, indent=2))) try: os.chmod(config_file, mode) except Exception as e: tb = traceback.format_exc() warnings.warn("Failed to set permissions on %s:\n%s" % (config_file, tb), RuntimeWarning)
def new_metadata(name=None, authors=None, license=None, created=None, modified=None, gistid=None): """Create a new metadata node.""" metadata = NotebookNode() if name is not None: metadata.name = cast_unicode(name) if authors is not None: metadata.authors = list(authors) if created is not None: metadata.created = cast_unicode(created) if modified is not None: metadata.modified = cast_unicode(modified) if license is not None: metadata.license = cast_unicode(license) if gistid is not None: metadata.gistid = cast_unicode(gistid) return metadata
def pre_get(self): # authenticate first super().pre_get() # check session collision: yield self._register_session() # then request kernel info, waiting up to a certain time before giving up. # We don't want to wait forever, because browsers don't take it well when # servers never respond to websocket connection requests. kernel = self.kernel_manager.get_kernel(self.kernel_id) self.session.key = kernel.session.key self.buffer_key = cast_unicode(kernel.session.key, "utf-8") future = self.request_kernel_info() def give_up(): """Don't wait forever for the kernel to reply""" if future.done(): return self.log.warning("Timeout waiting for kernel_info reply from %s", self.kernel_id) future.set_result({}) loop = IOLoop.current() loop.add_timeout(loop.time() + self.kernel_info_timeout, give_up) # actually wait for it yield future
def is_file_hidden_win(abs_path, stat_res=None): """Is a file hidden? This only checks the file itself; it should be called in combination with checking the directory containing the file. Use is_hidden() instead to check the file and its parent directories. Parameters ---------- abs_path : unicode The absolute path to check. stat_res : os.stat_result, optional Ignored on Windows, exists for compatibility with POSIX version of the function. """ if os.path.basename(abs_path).startswith('.'): return True win32_FILE_ATTRIBUTE_HIDDEN = 0x02 try: attrs = ctypes.windll.kernel32.GetFileAttributesW( py3compat.cast_unicode(abs_path) ) except AttributeError: pass else: if attrs > 0 and attrs & win32_FILE_ATTRIBUTE_HIDDEN: return True return False
def new_heading_cell(source=None, level=1, rendered=None, metadata=None): """Create a new section cell with a given integer level.""" cell = NotebookNode() cell.cell_type = u"heading" if source is not None: cell.source = cast_unicode(source) cell.level = int(level) cell.metadata = NotebookNode(metadata or {}) return cell
def test_is_hidden_win32(): with TemporaryDirectory() as root: root = cast_unicode(root) subdir1 = os.path.join(root, u'subdir') os.makedirs(subdir1) assert not is_hidden(subdir1, root) r = ctypes.windll.kernel32.SetFileAttributesW(subdir1, 0x02) print(r) assert is_hidden(subdir1, root)
def test_is_hidden_win32(tmp_path): root = str(tmp_path) root = cast_unicode(root) subdir1 = tmp_path / 'subdir' subdir1.mkdir() assert not is_hidden(str(subdir1), root) ctypes.windll.kernel32.SetFileAttributesW(str(subdir1), 0x02) assert is_hidden(str(subdir1), root) assert is_file_hidden(str(subdir1))
def new_heading_cell(source=None, level=1, rendered=None, metadata=None): """Create a new section cell with a given integer level.""" cell = NotebookNode() cell.cell_type = u'heading' if source is not None: cell.source = cast_unicode(source) cell.level = int(level) cell.metadata = NotebookNode(metadata or {}) return cell
def is_hidden(abs_path, abs_root=''): """Is a file hidden or contained in a hidden directory? This will start with the rightmost path element and work backwards to the given root to see if a path is hidden or in a hidden directory. Hidden is determined by either name starting with '.' or the UF_HIDDEN flag as reported by stat. Parameters ---------- abs_path : unicode The absolute path to check for hidden directories. abs_root : unicode The absolute path of the root directory in which hidden directories should be checked for. """ if not abs_root: abs_root = abs_path.split(os.sep, 1)[0] + os.sep inside_root = abs_path[len(abs_root):] if any(part.startswith('.') for part in inside_root.split(os.sep)): return True # check that dirs can be listed # may fail on Windows junctions or non-user-readable dirs if os.path.isdir(abs_path): try: os.listdir(abs_path) except OSError: return True # check UF_HIDDEN on any location up to root path = abs_path while path and path.startswith(abs_root) and path != abs_root: if not os.path.exists(path): path = os.path.dirname(path) continue try: # may fail on Windows junctions st = os.stat(path) except OSError: return True if getattr(st, 'st_flags', 0) & UF_HIDDEN: return True path = os.path.dirname(path) if sys.platform == 'win32': try: attrs = ctypes.windll.kernel32.GetFileAttributesW( py3compat.cast_unicode(path)) except AttributeError: pass else: if attrs > 0 and attrs & _win32_FILE_ATTRIBUTE_HIDDEN: return True return False
def new_code_cell(input=None, prompt_number=None, outputs=None, language=u"python", collapsed=False, metadata=None): """Create a new code cell with input and output""" cell = NotebookNode() cell.cell_type = u"code" if language is not None: cell.language = cast_unicode(language) if input is not None: cell.input = cast_unicode(input) if prompt_number is not None: cell.prompt_number = int(prompt_number) if outputs is None: cell.outputs = [] else: cell.outputs = outputs if collapsed is not None: cell.collapsed = bool(collapsed) cell.metadata = NotebookNode(metadata or {}) return cell
def is_hidden(abs_path, abs_root=''): """Is a file hidden or contained in a hidden directory? This will start with the rightmost path element and work backwards to the given root to see if a path is hidden or in a hidden directory. Hidden is determined by either name starting with '.' or the UF_HIDDEN flag as reported by stat. Parameters ---------- abs_path : unicode The absolute path to check for hidden directories. abs_root : unicode The absolute path of the root directory in which hidden directories should be checked for. """ if not abs_root: abs_root = abs_path.split(os.sep, 1)[0] + os.sep inside_root = abs_path[len(abs_root):] if any(part.startswith('.') for part in inside_root.split(os.sep)): return True # check that dirs can be listed # may fail on Windows junctions or non-user-readable dirs if os.path.isdir(abs_path): try: os.listdir(abs_path) except OSError: return True # check UF_HIDDEN on any location up to root path = abs_path while path and path.startswith(abs_root) and path != abs_root: if not os.path.exists(path): path = os.path.dirname(path) continue try: # may fail on Windows junctions st = os.stat(path) except OSError: return True if getattr(st, 'st_flags', 0) & UF_HIDDEN: return True path = os.path.dirname(path) if sys.platform == 'win32': try: attrs = ctypes.windll.kernel32.GetFileAttributesW(py3compat.cast_unicode(path)) except AttributeError: pass else: if attrs > 0 and attrs & _win32_FILE_ATTRIBUTE_HIDDEN: return True return False
def setup_kernel(cmd): """start an embedded kernel in a subprocess, and wait for it to be ready Returns ------- kernel_manager: connected KernelManager instance """ def connection_file_ready(connection_file): """Check if connection_file is a readable json file.""" if not os.path.exists(connection_file): return False try: with open(connection_file) as f: json.load(f) return True except ValueError: return False kernel = Popen([sys.executable, '-c', cmd], stdout=PIPE, stderr=PIPE) try: connection_file = os.path.join( paths.jupyter_runtime_dir(), 'kernel-%i.json' % kernel.pid, ) # wait for connection file to exist, timeout after 5s tic = time.time() while not connection_file_ready(connection_file) \ and kernel.poll() is None \ and time.time() < tic + SETUP_TIMEOUT: time.sleep(0.1) # Wait 100ms for the writing to finish time.sleep(0.1) if kernel.poll() is not None: o,e = kernel.communicate() e = py3compat.cast_unicode(e) raise IOError("Kernel failed to start:\n%s" % e) if not os.path.exists(connection_file): if kernel.poll() is None: kernel.terminate() raise IOError("Connection file %r never arrived" % connection_file) client = BlockingKernelClient(connection_file=connection_file) client.load_connection_file() client.start_channels() client.wait_for_ready() try: yield client finally: client.stop_channels() finally: kernel.terminate()
def passwd(passphrase=None, algorithm='argon2'): """Generate hashed password and salt for use in notebook configuration. In the notebook configuration, set `c.NotebookApp.password` to the generated string. Parameters ---------- passphrase : str Password to hash. If unspecified, the user is asked to input and verify a password. algorithm : str Hashing algorithm to use (e.g, 'sha1' or any argument supported by :func:`hashlib.new`, or 'argon2'). Returns ------- hashed_passphrase : str Hashed password, in the format 'hash_algorithm:salt:passphrase_hash'. Examples -------- >>> passwd('mypassword') 'sha1:7cf3:b7d6da294ea9592a9480c8f52e63cd42cfb9dd12' """ if passphrase is None: for i in range(3): p0 = getpass.getpass('Enter password: '******'Verify password: '******'Passwords do not match.') else: raise ValueError('No matching passwords found. Giving up.') if algorithm == 'argon2': from argon2 import PasswordHasher ph = PasswordHasher( memory_cost=10240, time_cost=10, parallelism=8, ) h = ph.hash(passphrase) return ':'.join((algorithm, cast_unicode(h, 'ascii'))) else: h = hashlib.new(algorithm) salt = ('%0' + str(salt_len) + 'x') % random.getrandbits(4 * salt_len) h.update(cast_bytes(passphrase, 'utf-8') + str_to_bytes(salt, 'ascii')) return ':'.join((algorithm, salt, h.hexdigest()))
def new_text_cell(cell_type, source=None, rendered=None, metadata=None): """Create a new text cell.""" cell = NotebookNode() # VERSIONHACK: plaintext -> raw # handle never-released plaintext name for raw cells if cell_type == "plaintext": cell_type = "raw" if source is not None: cell.source = cast_unicode(source) cell.metadata = NotebookNode(metadata or {}) cell.cell_type = cell_type return cell
def new_text_cell(cell_type, source=None, rendered=None, metadata=None): """Create a new text cell.""" cell = NotebookNode() # VERSIONHACK: plaintext -> raw # handle never-released plaintext name for raw cells if cell_type == 'plaintext': cell_type = 'raw' if source is not None: cell.source = cast_unicode(source) cell.metadata = NotebookNode(metadata or {}) cell.cell_type = cell_type return cell
def new_code_cell(input=None, prompt_number=None, outputs=None, language=u'python', collapsed=False, metadata=None): """Create a new code cell with input and output""" cell = NotebookNode() cell.cell_type = u'code' if language is not None: cell.language = cast_unicode(language) if input is not None: cell.input = cast_unicode(input) if prompt_number is not None: cell.prompt_number = int(prompt_number) if outputs is None: cell.outputs = [] else: cell.outputs = outputs if collapsed is not None: cell.collapsed = bool(collapsed) cell.metadata = NotebookNode(metadata or {}) return cell
def start(self): if self.reset: if os.path.exists(self.notary.db_file): print("Removing trusted signature cache: %s" % self.notary.db_file) os.remove(self.notary.db_file) self.generate_new_key() return if not self.extra_args: self.log.debug("Reading notebook from stdin") nb_s = cast_unicode(sys.stdin.read()) nb = reads(nb_s, NO_CONVERT) self.sign_notebook(nb, '<stdin>') else: for notebook_path in self.extra_args: self.sign_notebook_file(notebook_path)
def pre_get(self): """Run before finishing the GET request Extend this method to add logic that should fire before the websocket finishes completing. """ # authenticate the request before opening the websocket if self.get_current_user() is None: self.log.warning("Couldn't authenticate WebSocket connection") raise web.HTTPError(403) if self.get_argument('session_id', False): self.session.session = cast_unicode(self.get_argument('session_id')) else: self.log.warning("No session ID specified")
def authenticate(self): """Run before finishing the GET request Extend this method to add logic that should fire before the websocket finishes completing. """ # authenticate the request before opening the websocket if self.get_current_user() is None: self.log.warning("Couldn't authenticate WebSocket connection") raise web.HTTPError(403) if self.get_argument('session_id', False): self.session.session = cast_unicode(self.get_argument('session_id')) else: self.log.warning("No session ID specified")
def new_notebook(name=None, metadata=None, worksheets=None): """Create a notebook by name, id and a list of worksheets.""" nb = NotebookNode() nb.nbformat = nbformat nb.nbformat_minor = nbformat_minor if worksheets is None: nb.worksheets = [] else: nb.worksheets = list(worksheets) if metadata is None: nb.metadata = new_metadata() else: nb.metadata = NotebookNode(metadata) if name is not None: nb.metadata.name = cast_unicode(name) return nb
def setUp(self): """Build an isolated config environment.""" td = TemporaryDirectory() self.test_dir = py3compat.cast_unicode(td.name) self.data_dir = os.path.join(self.test_dir, 'data') self.config_dir = os.path.join(self.test_dir, 'config') self.system_data_dir = os.path.join(self.test_dir, 'system_data') self.system_path = [self.system_data_dir] # Use temp directory, not real user or system config paths self.patch_env = patch.dict( 'os.environ', { 'JUPYTER_CONFIG_DIR': self.config_dir, 'JUPYTER_DATA_DIR': self.data_dir, }) self.patch_env.start()
def test_get_nb_invalid(self): nb = { 'nbformat': 4, 'metadata': {}, 'cells': [{ 'cell_type': 'wrong', 'metadata': {}, }], } path = u'å b/Validate tést.ipynb' self.make_txt(path, py3compat.cast_unicode(json.dumps(nb))) model = self.api.read(path).json() self.assertEqual(model['path'], path) self.assertEqual(model['type'], 'notebook') self.assertIn('content', model) self.assertIn('message', model) self.assertIn("validation failed", model['message'].lower())
def _reserialize_reply(self, msg_list, channel=None): """Reserialize a reply message using JSON. This takes the msg list from the ZMQ socket, deserializes it using self.session and then serializes the result using JSON. This method should be used by self._on_zmq_reply to build messages that can be sent back to the browser. """ idents, msg_list = self.session.feed_identities(msg_list) msg = self.session.deserialize(msg_list) if channel: msg['channel'] = channel if msg['buffers']: buf = serialize_binary_message(msg) return buf else: smsg = json.dumps(msg, default=date_default) return cast_unicode(smsg)
def _reserialize_reply(self, msg_list, channel=None): """Reserialize a reply message using JSON. This takes the msg list from the ZMQ socket, deserializes it using self.session and then serializes the result using JSON. This method should be used by self._on_zmq_reply to build messages that can be sent back to the browser. """ idents, msg_list = self.session.feed_identities(msg_list) msg = self.session.deserialize(msg_list) if channel: msg["channel"] = channel if msg["buffers"]: buf = serialize_binary_message(msg) return buf else: smsg = json.dumps(msg, default=date_default) return cast_unicode(smsg)
def setup_kernel(cmd): """start an embedded kernel in a subprocess, and wait for it to be ready This function was taken from the ipykernel project. We plan to remove it when dropping support for python 2. Returns ------- kernel_manager: connected KernelManager instance """ kernel = Popen([sys.executable, '-c', cmd], stdout=PIPE, stderr=PIPE) try: connection_file = os.path.join( paths.jupyter_runtime_dir(), 'kernel-%i.json' % kernel.pid, ) # wait for connection file to exist, timeout after 5s tic = time.time() while not os.path.exists(connection_file) \ and kernel.poll() is None \ and time.time() < tic + SETUP_TIMEOUT: time.sleep(0.1) if kernel.poll() is not None: o, e = kernel.communicate() e = py3compat.cast_unicode(e) raise IOError("Kernel failed to start:\n%s" % e) if not os.path.exists(connection_file): if kernel.poll() is None: kernel.terminate() raise IOError("Connection file %r never arrived" % connection_file) client = BlockingKernelClient(connection_file=connection_file) client.load_connection_file() client.start_channels() client.wait_for_ready() try: yield client finally: client.stop_channels() finally: kernel.terminate()
def setUp(self): """Build an isolated config environment.""" td = TemporaryDirectory() self.test_dir = py3compat.cast_unicode(td.name) self.data_dir = os.path.join(self.test_dir, 'data') self.config_dir = os.path.join(self.test_dir, 'config') self.system_data_dir = os.path.join(self.test_dir, 'system_data') self.system_path = [self.system_data_dir] # Use temp directory, not real user or system config paths self.patch_env = patch.dict('os.environ', { 'JUPYTER_CONFIG_DIR': self.config_dir, 'JUPYTER_DATA_DIR': self.data_dir, }) self.patch_env.start() self.patch_system_path = patch.object(nbextensions, 'SYSTEM_JUPYTER_PATH', self.system_path) self.patch_system_path.start()
def rlcomplete(self, text, state): if state == 0: line = str_to_unicode(readline.get_line_buffer()) byte_cursor_pos = readline.get_endidx() # get_endidx is a byte offset # account for multi-byte characters to get correct cursor_pos bytes_before_cursor = cast_bytes(line)[:byte_cursor_pos] cursor_pos = len(cast_unicode(bytes_before_cursor)) try: content = self.complete_request(line, cursor_pos) self.matches = _construct_readline_matches(line, cursor_pos, content) except Empty: #print('WARNING: Kernel timeout on tab completion.') pass try: return self.matches[state] except IndexError: return None
def parse_command_line(self, argv=None): """Parse the command line arguments.""" argv = sys.argv[1:] if argv is None else argv self.argv = [py3compat.cast_unicode(arg) for arg in argv] if argv and argv[0] == 'help': # turn `ipython help notebook` into `ipython notebook -h` argv = argv[1:] + ['-h'] if self.subcommands and len(argv) > 0: # we have subcommands, and one may have been specified subc, subargv = argv[0], argv[1:] if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands: # it's a subcommand, and *not* a flag or class parameter return self.initialize_subcommand(subc, subargv) # Arguments after a '--' argument are for the script IPython may be # about to run, not IPython iteslf. For arguments parsed here (help and # version), we want to only search the arguments up to the first # occurrence of '--', which we're calling interpreted_argv. try: interpreted_argv = argv[:argv.index('--')] except ValueError: interpreted_argv = argv if any(x in interpreted_argv for x in ('-h', '--help-all', '--help')): self.print_help('--help-all' in interpreted_argv) self.exit(0) if '--version' in interpreted_argv or '-V' in interpreted_argv: self.print_version() self.exit(0) # flatten flags&aliases, so cl-args get appropriate priority: flags, aliases = self.flatten_flags() loader = KVArgParseConfigLoader(argv=argv, aliases=aliases, flags=flags, log=self.log) config = loader.load_config() self.update_config(config) # store unparsed args in extra_args self.extra_args = loader.extra_args
def rlcomplete(self, text, state): if state == 0: line = str_to_unicode(readline.get_line_buffer()) byte_cursor_pos = readline.get_endidx() # get_endidx is a byte offset # account for multi-byte characters to get correct cursor_pos bytes_before_cursor = cast_bytes(line)[:byte_cursor_pos] cursor_pos = len(cast_unicode(bytes_before_cursor)) try: content = self.complete_request(line, cursor_pos) self.matches = _construct_readline_matches( line, cursor_pos, content) except Empty: #print('WARNING: Kernel timeout on tab completion.') pass try: return self.matches[state] except IndexError: return None
def parse_command_line(self, argv=None): """Parse the command line arguments.""" argv = sys.argv[1:] if argv is None else argv self.argv = [ py3compat.cast_unicode(arg) for arg in argv ] if argv and argv[0] == 'help': # turn `ipython help notebook` into `ipython notebook -h` argv = argv[1:] + ['-h'] if self.subcommands and len(argv) > 0: # we have subcommands, and one may have been specified subc, subargv = argv[0], argv[1:] if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands: # it's a subcommand, and *not* a flag or class parameter return self.initialize_subcommand(subc, subargv) # Arguments after a '--' argument are for the script IPython may be # about to run, not IPython iteslf. For arguments parsed here (help and # version), we want to only search the arguments up to the first # occurrence of '--', which we're calling interpreted_argv. try: interpreted_argv = argv[:argv.index('--')] except ValueError: interpreted_argv = argv if any(x in interpreted_argv for x in ('-h', '--help-all', '--help')): self.print_help('--help-all' in interpreted_argv) self.exit(0) if '--version' in interpreted_argv or '-V' in interpreted_argv: self.print_version() self.exit(0) # flatten flags&aliases, so cl-args get appropriate priority: flags,aliases = self.flatten_flags() loader = KVArgParseConfigLoader(argv=argv, aliases=aliases, flags=flags, log=self.log) config = loader.load_config() self.update_config(config) # store unparsed args in extra_args self.extra_args = loader.extra_args
def setup_kernel(cmd): """start an embedded kernel in a subprocess, and wait for it to be ready Returns ------- kernel_manager: connected KernelManager instance """ kernel = Popen([sys.executable, '-c', cmd], stdout=PIPE, stderr=PIPE) connection_file = os.path.join( paths.jupyter_runtime_dir(), 'kernel-%i.json' % kernel.pid, ) # wait for connection file to exist, timeout after 5s tic = time.time() while not os.path.exists(connection_file) \ and kernel.poll() is None \ and time.time() < tic + SETUP_TIMEOUT: time.sleep(0.1) if kernel.poll() is not None: o,e = kernel.communicate() e = py3compat.cast_unicode(e) raise IOError("Kernel failed to start:\n%s" % e) if not os.path.exists(connection_file): if kernel.poll() is None: kernel.terminate() raise IOError("Connection file %r never arrived" % connection_file) client = BlockingKernelClient(connection_file=connection_file) client.load_connection_file() client.start_channels() client.wait_for_ready() try: yield client finally: client.stop_channels() kernel.terminate()
def is_file_hidden_win(abs_path, stat_res=None): """Is a file hidden? This only checks the file itself; it should be called in combination with checking the directory containing the file. Use is_hidden() instead to check the file and its parent directories. Parameters ---------- abs_path : unicode The absolute path to check. stat_res : os.stat_result, optional Ignored on Windows, exists for compatibility with POSIX version of the function. """ if os.path.basename(abs_path).startswith('.'): return True # check that dirs can be listed if os.path.isdir(abs_path): # can't trust os.access on Windows because it seems to always return True try: os.stat(abs_path) except OSError: # stat may fail on Windows junctions or non-user-readable dirs return True try: attrs = ctypes.windll.kernel32.GetFileAttributesW( py3compat.cast_unicode(abs_path)) except AttributeError: pass else: if attrs > 0 and attrs & _win32_FILE_ATTRIBUTE_HIDDEN: return True return False
def ascii_only(s): """ensure a string is ascii""" s = py3compat.cast_unicode(s) return s.encode('ascii', 'replace').decode('ascii')
def get(self, kernel_id, *args, **kwargs): self.authenticate() self.kernel_id = cast_unicode(kernel_id, 'ascii') super(WebSocketChannelsHandler, self).get(kernel_id=kernel_id, *args, **kwargs)
def tempdir(self): td = TemporaryDirectory() self.tempdirs.append(td) return py3compat.cast_unicode(td.name)
def new_output( output_type, output_text=None, output_png=None, output_html=None, output_svg=None, output_latex=None, output_json=None, output_javascript=None, output_jpeg=None, prompt_number=None, ename=None, evalue=None, traceback=None, stream=None, metadata=None, ): """Create a new output, to go in the ``cell.outputs`` list of a code cell. """ output = NotebookNode() output.output_type = unicode_type(output_type) if metadata is None: metadata = {} if not isinstance(metadata, dict): raise TypeError("metadata must be dict") if output_type in {u"pyout", "display_data"}: output.metadata = metadata if output_type != "pyerr": if output_text is not None: output.text = cast_unicode(output_text) if output_png is not None: output.png = cast_unicode(output_png) if output_jpeg is not None: output.jpeg = cast_unicode(output_jpeg) if output_html is not None: output.html = cast_unicode(output_html) if output_svg is not None: output.svg = cast_unicode(output_svg) if output_latex is not None: output.latex = cast_unicode(output_latex) if output_json is not None: output.json = cast_unicode(output_json) if output_javascript is not None: output.javascript = cast_unicode(output_javascript) if output_type == u"pyout": if prompt_number is not None: output.prompt_number = int(prompt_number) if output_type == u"pyerr": if ename is not None: output.ename = cast_unicode(ename) if evalue is not None: output.evalue = cast_unicode(evalue) if traceback is not None: output.traceback = [cast_unicode(frame) for frame in list(traceback)] if output_type == u"stream": output.stream = "stdout" if stream is None else cast_unicode(stream) return output
def get(self, kernel_id): self.kernel_id = cast_unicode(kernel_id, 'ascii') yield super(ZMQChannelsHandler, self).get(kernel_id=kernel_id)
class APITest(NotebookTestBase): """Test the kernels web service API""" dirs_nbs = [ ('', 'inroot'), ('Directory with spaces in', 'inspace'), (u'unicodé', 'innonascii'), ('foo', 'a'), ('foo', 'b'), ('foo', 'name with spaces'), ('foo', u'unicodé'), ('foo/bar', 'baz'), ('ordering', 'A'), ('ordering', 'b'), ('ordering', 'C'), (u'å b', u'ç d'), ] hidden_dirs = ['.hidden', '__pycache__'] # Don't include root dir. dirs = uniq_stable([py3compat.cast_unicode(d) for (d, n) in dirs_nbs[1:]]) top_level_dirs = {normalize('NFC', d.split('/')[0]) for d in dirs} @staticmethod def _blob_for_name(name): return name.encode('utf-8') + b'\xFF' @staticmethod def _txt_for_name(name): return u'%s text file' % name def to_os_path(self, api_path): return to_os_path(api_path, root=self.notebook_dir) def make_dir(self, api_path): """Create a directory at api_path""" os_path = self.to_os_path(api_path) try: os.makedirs(os_path) except OSError: print("Directory already exists: %r" % os_path) def make_txt(self, api_path, txt): """Make a text file at a given api_path""" os_path = self.to_os_path(api_path) with io.open(os_path, 'w', encoding='utf-8') as f: f.write(txt) def make_blob(self, api_path, blob): """Make a binary file at a given api_path""" os_path = self.to_os_path(api_path) with io.open(os_path, 'wb') as f: f.write(blob) def make_nb(self, api_path, nb): """Make a notebook file at a given api_path""" os_path = self.to_os_path(api_path) with io.open(os_path, 'w', encoding='utf-8') as f: write(nb, f, version=4) def delete_dir(self, api_path): """Delete a directory at api_path, removing any contents.""" os_path = self.to_os_path(api_path) shutil.rmtree(os_path, ignore_errors=True) def delete_file(self, api_path): """Delete a file at the given path if it exists.""" if self.isfile(api_path): os.unlink(self.to_os_path(api_path)) def isfile(self, api_path): return os.path.isfile(self.to_os_path(api_path)) def isdir(self, api_path): return os.path.isdir(self.to_os_path(api_path)) def setUp(self): for d in (self.dirs + self.hidden_dirs): self.make_dir(d) self.addCleanup(partial(self.delete_dir, d)) for d, name in self.dirs_nbs: # create a notebook nb = new_notebook() nbname = u'{}/{}.ipynb'.format(d, name) self.make_nb(nbname, nb) self.addCleanup(partial(self.delete_file, nbname)) # create a text file txt = self._txt_for_name(name) txtname = u'{}/{}.txt'.format(d, name) self.make_txt(txtname, txt) self.addCleanup(partial(self.delete_file, txtname)) blob = self._blob_for_name(name) blobname = u'{}/{}.blob'.format(d, name) self.make_blob(blobname, blob) self.addCleanup(partial(self.delete_file, blobname)) self.api = API(self.request) def test_list_notebooks(self): nbs = notebooks_only(self.api.list().json()) self.assertEqual(len(nbs), 1) self.assertEqual(nbs[0]['name'], 'inroot.ipynb') nbs = notebooks_only( self.api.list('/Directory with spaces in/').json()) self.assertEqual(len(nbs), 1) self.assertEqual(nbs[0]['name'], 'inspace.ipynb') nbs = notebooks_only(self.api.list(u'/unicodé/').json()) self.assertEqual(len(nbs), 1) self.assertEqual(nbs[0]['name'], 'innonascii.ipynb') self.assertEqual(nbs[0]['path'], u'unicodé/innonascii.ipynb') nbs = notebooks_only(self.api.list('/foo/bar/').json()) self.assertEqual(len(nbs), 1) self.assertEqual(nbs[0]['name'], 'baz.ipynb') self.assertEqual(nbs[0]['path'], 'foo/bar/baz.ipynb') nbs = notebooks_only(self.api.list('foo').json()) self.assertEqual(len(nbs), 4) nbnames = {normalize('NFC', n['name']) for n in nbs} expected = [ u'a.ipynb', u'b.ipynb', u'name with spaces.ipynb', u'unicodé.ipynb' ] expected = {normalize('NFC', name) for name in expected} self.assertEqual(nbnames, expected) nbs = notebooks_only(self.api.list('ordering').json()) nbnames = {n['name'] for n in nbs} expected = {'A.ipynb', 'b.ipynb', 'C.ipynb'} self.assertEqual(nbnames, expected) def test_list_dirs(self): dirs = dirs_only(self.api.list().json()) dir_names = {normalize('NFC', d['name']) for d in dirs} self.assertEqual(dir_names, self.top_level_dirs) # Excluding hidden dirs def test_get_dir_no_content(self): for d in self.dirs: model = self.api.read(d, content=False).json() self.assertEqual(model['path'], d) self.assertEqual(model['type'], 'directory') self.assertIn('content', model) self.assertEqual(model['content'], None) def test_list_nonexistant_dir(self): with assert_http_error(404): self.api.list('nonexistant') def test_get_nb_contents(self): for d, name in self.dirs_nbs: path = url_path_join(d, name + '.ipynb') nb = self.api.read(path).json() self.assertEqual(nb['name'], u'%s.ipynb' % name) self.assertEqual(nb['path'], path) self.assertEqual(nb['type'], 'notebook') self.assertIn('content', nb) self.assertEqual(nb['format'], 'json') self.assertIn('metadata', nb['content']) self.assertIsInstance(nb['content']['metadata'], dict) def test_get_nb_no_content(self): for d, name in self.dirs_nbs: path = url_path_join(d, name + '.ipynb') nb = self.api.read(path, content=False).json() self.assertEqual(nb['name'], u'%s.ipynb' % name) self.assertEqual(nb['path'], path) self.assertEqual(nb['type'], 'notebook') self.assertIn('content', nb) self.assertEqual(nb['content'], None) def test_get_nb_invalid(self): nb = { 'nbformat': 4, 'metadata': {}, 'cells': [{ 'cell_type': 'wrong', 'metadata': {}, }], } path = u'å b/Validate tést.ipynb' self.make_txt(path, py3compat.cast_unicode(json.dumps(nb))) model = self.api.read(path).json() self.assertEqual(model['path'], path) self.assertEqual(model['type'], 'notebook') self.assertIn('content', model) self.assertIn('message', model) self.assertIn("validation failed", model['message'].lower()) def test_get_contents_no_such_file(self): # Name that doesn't exist - should be a 404 with assert_http_error(404): self.api.read('foo/q.ipynb') def test_get_text_file_contents(self): for d, name in self.dirs_nbs: path = url_path_join(d, name + '.txt') model = self.api.read(path).json() self.assertEqual(model['name'], u'%s.txt' % name) self.assertEqual(model['path'], path) self.assertIn('content', model) self.assertEqual(model['format'], 'text') self.assertEqual(model['type'], 'file') self.assertEqual(model['content'], self._txt_for_name(name)) # Name that doesn't exist - should be a 404 with assert_http_error(404): self.api.read('foo/q.txt') # Specifying format=text should fail on a non-UTF-8 file with assert_http_error(400): self.api.read('foo/bar/baz.blob', type='file', format='text') def test_get_binary_file_contents(self): for d, name in self.dirs_nbs: path = url_path_join(d, name + '.blob') model = self.api.read(path).json() self.assertEqual(model['name'], u'%s.blob' % name) self.assertEqual(model['path'], path) self.assertIn('content', model) self.assertEqual(model['format'], 'base64') self.assertEqual(model['type'], 'file') self.assertEqual( decodebytes(model['content'].encode('ascii')), self._blob_for_name(name), ) # Name that doesn't exist - should be a 404 with assert_http_error(404): self.api.read('foo/q.txt') def test_get_bad_type(self): with assert_http_error(400): self.api.read(u'unicodé', type='file') # this is a directory with assert_http_error(400): self.api.read(u'unicodé/innonascii.ipynb', type='directory') def _check_created(self, resp, path, type='notebook'): self.assertEqual(resp.status_code, 201) location_header = py3compat.str_to_unicode(resp.headers['Location']) self.assertEqual( location_header, url_path_join(self.url_prefix, u'api/contents', url_escape(path))) rjson = resp.json() self.assertEqual(rjson['name'], path.rsplit('/', 1)[-1]) self.assertEqual(rjson['path'], path) self.assertEqual(rjson['type'], type) isright = self.isdir if type == 'directory' else self.isfile assert isright(path) def test_create_untitled(self): resp = self.api.create_untitled(path=u'å b') self._check_created(resp, u'å b/Untitled.ipynb') # Second time resp = self.api.create_untitled(path=u'å b') self._check_created(resp, u'å b/Untitled1.ipynb') # And two directories down resp = self.api.create_untitled(path='foo/bar') self._check_created(resp, 'foo/bar/Untitled.ipynb') def test_create_untitled_txt(self): resp = self.api.create_untitled(path='foo/bar', ext='.txt') self._check_created(resp, 'foo/bar/untitled.txt', type='file') resp = self.api.read(path='foo/bar/untitled.txt') model = resp.json() self.assertEqual(model['type'], 'file') self.assertEqual(model['format'], 'text') self.assertEqual(model['content'], '') def test_upload(self): nb = new_notebook() nbmodel = {'content': nb, 'type': 'notebook'} path = u'å b/Upload tést.ipynb' resp = self.api.upload(path, body=json.dumps(nbmodel)) self._check_created(resp, path) def test_mkdir_untitled(self): resp = self.api.mkdir_untitled(path=u'å b') self._check_created(resp, u'å b/Untitled Folder', type='directory') # Second time resp = self.api.mkdir_untitled(path=u'å b') self._check_created(resp, u'å b/Untitled Folder 1', type='directory') # And two directories down resp = self.api.mkdir_untitled(path='foo/bar') self._check_created(resp, 'foo/bar/Untitled Folder', type='directory') def test_mkdir(self): path = u'å b/New ∂ir' resp = self.api.mkdir(path) self._check_created(resp, path, type='directory') def test_mkdir_hidden_400(self): with assert_http_error(400): resp = self.api.mkdir(u'å b/.hidden') def test_upload_txt(self): body = u'ünicode téxt' model = { 'content': body, 'format': 'text', 'type': 'file', } path = u'å b/Upload tést.txt' resp = self.api.upload(path, body=json.dumps(model)) # check roundtrip resp = self.api.read(path) model = resp.json() self.assertEqual(model['type'], 'file') self.assertEqual(model['format'], 'text') self.assertEqual(model['content'], body) def test_upload_b64(self): body = b'\xFFblob' b64body = encodebytes(body).decode('ascii') model = { 'content': b64body, 'format': 'base64', 'type': 'file', } path = u'å b/Upload tést.blob' resp = self.api.upload(path, body=json.dumps(model)) # check roundtrip resp = self.api.read(path) model = resp.json() self.assertEqual(model['type'], 'file') self.assertEqual(model['path'], path) self.assertEqual(model['format'], 'base64') decoded = decodebytes(model['content'].encode('ascii')) self.assertEqual(decoded, body) def test_upload_v2(self): nb = v2.new_notebook() ws = v2.new_worksheet() nb.worksheets.append(ws) ws.cells.append(v2.new_code_cell(input='print("hi")')) nbmodel = {'content': nb, 'type': 'notebook'} path = u'å b/Upload tést.ipynb' resp = self.api.upload(path, body=json.dumps(nbmodel)) self._check_created(resp, path) resp = self.api.read(path) data = resp.json() self.assertEqual(data['content']['nbformat'], 4) def test_copy(self): resp = self.api.copy(u'å b/ç d.ipynb', u'å b') self._check_created(resp, u'å b/ç d-Copy1.ipynb') resp = self.api.copy(u'å b/ç d.ipynb', u'å b') self._check_created(resp, u'å b/ç d-Copy2.ipynb') def test_copy_copy(self): resp = self.api.copy(u'å b/ç d.ipynb', u'å b') self._check_created(resp, u'å b/ç d-Copy1.ipynb') resp = self.api.copy(u'å b/ç d-Copy1.ipynb', u'å b') self._check_created(resp, u'å b/ç d-Copy2.ipynb') def test_copy_path(self): resp = self.api.copy(u'foo/a.ipynb', u'å b') self._check_created(resp, u'å b/a.ipynb') resp = self.api.copy(u'foo/a.ipynb', u'å b') self._check_created(resp, u'å b/a-Copy1.ipynb') def test_copy_put_400(self): with assert_http_error(400): resp = self.api.copy_put(u'å b/ç d.ipynb', u'å b/cøpy.ipynb') def test_copy_dir_400(self): # can't copy directories with assert_http_error(400): resp = self.api.copy(u'å b', u'foo') def test_delete(self): for d, name in self.dirs_nbs: print('%r, %r' % (d, name)) resp = self.api.delete(url_path_join(d, name + '.ipynb')) self.assertEqual(resp.status_code, 204) for d in self.dirs + ['/']: nbs = notebooks_only(self.api.list(d).json()) print('------') print(d) print(nbs) self.assertEqual(nbs, []) def test_delete_dirs(self): # depth-first delete everything, so we don't try to delete empty directories for name in sorted(self.dirs + ['/'], key=len, reverse=True): listing = self.api.list(name).json()['content'] for model in listing: self.api.delete(model['path']) listing = self.api.list('/').json()['content'] self.assertEqual(listing, []) def test_delete_non_empty_dir(self): # Test that non empty directory can be deleted self.api.delete(u'å b') # Check if directory has actually been deleted with assert_http_error(404): self.api.list(u'å b') def test_rename(self): resp = self.api.rename('foo/a.ipynb', 'foo/z.ipynb') self.assertEqual(resp.headers['Location'].split('/')[-1], 'z.ipynb') self.assertEqual(resp.json()['name'], 'z.ipynb') self.assertEqual(resp.json()['path'], 'foo/z.ipynb') assert self.isfile('foo/z.ipynb') nbs = notebooks_only(self.api.list('foo').json()) nbnames = set(n['name'] for n in nbs) self.assertIn('z.ipynb', nbnames) self.assertNotIn('a.ipynb', nbnames) def test_checkpoints_follow_file(self): # Read initial file state orig = self.api.read('foo/a.ipynb') # Create a checkpoint of initial state r = self.api.new_checkpoint('foo/a.ipynb') cp1 = r.json() # Modify file and save nbcontent = json.loads(orig.text)['content'] nb = from_dict(nbcontent) hcell = new_markdown_cell('Created by test') nb.cells.append(hcell) nbmodel = {'content': nb, 'type': 'notebook'} self.api.save('foo/a.ipynb', body=json.dumps(nbmodel)) # Rename the file. self.api.rename('foo/a.ipynb', 'foo/z.ipynb') # Looking for checkpoints in the old location should yield no results. self.assertEqual(self.api.get_checkpoints('foo/a.ipynb').json(), []) # Looking for checkpoints in the new location should work. cps = self.api.get_checkpoints('foo/z.ipynb').json() self.assertEqual(cps, [cp1]) # Delete the file. The checkpoint should be deleted as well. self.api.delete('foo/z.ipynb') cps = self.api.get_checkpoints('foo/z.ipynb').json() self.assertEqual(cps, []) def test_rename_existing(self): with assert_http_error(409): self.api.rename('foo/a.ipynb', 'foo/b.ipynb') def test_save(self): resp = self.api.read('foo/a.ipynb') nbcontent = json.loads(resp.text)['content'] nb = from_dict(nbcontent) nb.cells.append(new_markdown_cell(u'Created by test ³')) nbmodel = {'content': nb, 'type': 'notebook'} resp = self.api.save('foo/a.ipynb', body=json.dumps(nbmodel)) nbcontent = self.api.read('foo/a.ipynb').json()['content'] newnb = from_dict(nbcontent) self.assertEqual(newnb.cells[0].source, u'Created by test ³') def test_checkpoints(self): resp = self.api.read('foo/a.ipynb') r = self.api.new_checkpoint('foo/a.ipynb') self.assertEqual(r.status_code, 201) cp1 = r.json() self.assertEqual(set(cp1), {'id', 'last_modified'}) self.assertEqual(r.headers['Location'].split('/')[-1], cp1['id']) # Modify it nbcontent = json.loads(resp.text)['content'] nb = from_dict(nbcontent) hcell = new_markdown_cell('Created by test') nb.cells.append(hcell) # Save nbmodel = {'content': nb, 'type': 'notebook'} resp = self.api.save('foo/a.ipynb', body=json.dumps(nbmodel)) # List checkpoints cps = self.api.get_checkpoints('foo/a.ipynb').json() self.assertEqual(cps, [cp1]) nbcontent = self.api.read('foo/a.ipynb').json()['content'] nb = from_dict(nbcontent) self.assertEqual(nb.cells[0].source, 'Created by test') # Restore cp1 r = self.api.restore_checkpoint('foo/a.ipynb', cp1['id']) self.assertEqual(r.status_code, 204) nbcontent = self.api.read('foo/a.ipynb').json()['content'] nb = from_dict(nbcontent) self.assertEqual(nb.cells, []) # Delete cp1 r = self.api.delete_checkpoint('foo/a.ipynb', cp1['id']) self.assertEqual(r.status_code, 204) cps = self.api.get_checkpoints('foo/a.ipynb').json() self.assertEqual(cps, []) def test_file_checkpoints(self): """ Test checkpointing of non-notebook files. """ filename = 'foo/a.txt' resp = self.api.read(filename) orig_content = json.loads(resp.text)['content'] # Create a checkpoint. r = self.api.new_checkpoint(filename) self.assertEqual(r.status_code, 201) cp1 = r.json() self.assertEqual(set(cp1), {'id', 'last_modified'}) self.assertEqual(r.headers['Location'].split('/')[-1], cp1['id']) # Modify the file and save. new_content = orig_content + '\nsecond line' model = { 'content': new_content, 'type': 'file', 'format': 'text', } resp = self.api.save(filename, body=json.dumps(model)) # List checkpoints cps = self.api.get_checkpoints(filename).json() self.assertEqual(cps, [cp1]) content = self.api.read(filename).json()['content'] self.assertEqual(content, new_content) # Restore cp1 r = self.api.restore_checkpoint(filename, cp1['id']) self.assertEqual(r.status_code, 204) restored_content = self.api.read(filename).json()['content'] self.assertEqual(restored_content, orig_content) # Delete cp1 r = self.api.delete_checkpoint(filename, cp1['id']) self.assertEqual(r.status_code, 204) cps = self.api.get_checkpoints(filename).json() self.assertEqual(cps, []) @contextmanager def patch_cp_root(self, dirname): """ Temporarily patch the root dir of our checkpoint manager. """ cpm = self.notebook.contents_manager.checkpoints old_dirname = cpm.root_dir cpm.root_dir = dirname try: yield finally: cpm.root_dir = old_dirname def test_checkpoints_separate_root(self): """ Test that FileCheckpoints functions correctly even when it's using a different root dir from FileContentsManager. This also keeps the implementation honest for use with ContentsManagers that don't map models to the filesystem Override this method to a no-op when testing other managers. """ with TemporaryDirectory() as td: with self.patch_cp_root(td): self.test_checkpoints() with TemporaryDirectory() as td: with self.patch_cp_root(td): self.test_file_checkpoints()
def _parse_args(self, args): """self.parser->self.parsed_data""" # decode sys.argv to support unicode command-line options enc = DEFAULT_ENCODING uargs = [py3compat.cast_unicode(a, enc) for a in args] self.parsed_data, self.extra_args = self.parser.parse_known_args(uargs)
def export(self): """ Displays a dialog for exporting HTML generated by Qt's rich text system. Returns ------- The name of the file that was saved, or None if no file was saved. """ parent = self.control.window() dialog = QtGui.QFileDialog(parent, "Save as...") dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) filters = ["HTML with PNG figures (*.html *.htm)", "XHTML with inline SVG figures (*.xhtml *.xml)"] dialog.setNameFilters(filters) if self.filename: dialog.selectFile(self.filename) root, ext = os.path.splitext(self.filename) if ext.lower() in (".xml", ".xhtml"): dialog.selectNameFilter(filters[-1]) if dialog.exec_(): self.filename = dialog.selectedFiles()[0] choice = dialog.selectedNameFilter() html = py3compat.cast_unicode(self.control.document().toHtml()) # Configure the exporter. if choice.startswith("XHTML"): exporter = export_xhtml else: # If there are PNGs, decide how to export them. inline = self.inline_png if inline is None and IMG_RE.search(html): dialog = QtGui.QDialog(parent) dialog.setWindowTitle("Save as...") layout = QtGui.QVBoxLayout(dialog) msg = "Exporting HTML with PNGs" info = "Would you like inline PNGs (single large html " "file) or external image files?" checkbox = QtGui.QCheckBox("&Don't ask again") checkbox.setShortcut("D") ib = QtGui.QPushButton("&Inline") ib.setShortcut("I") eb = QtGui.QPushButton("&External") eb.setShortcut("E") box = QtGui.QMessageBox(QtGui.QMessageBox.Question, dialog.windowTitle(), msg) box.setInformativeText(info) box.addButton(ib, QtGui.QMessageBox.NoRole) box.addButton(eb, QtGui.QMessageBox.YesRole) layout.setSpacing(0) layout.addWidget(box) layout.addWidget(checkbox) dialog.setLayout(layout) dialog.show() reply = box.exec_() dialog.hide() inline = reply == 0 if checkbox.checkState(): # Don't ask anymore; always use this choice. self.inline_png = inline exporter = lambda h, f, i: export_html(h, f, i, inline) # Perform the export! try: return exporter(html, self.filename, self.image_tag) except Exception as e: msg = "Error exporting HTML to %s\n" % self.filename + str(e) reply = QtGui.QMessageBox.warning(parent, "Error", msg, QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok) return None