def save(self) -> Optional[PromptResult]: self.file.finalize_previous_action() # TODO: make directories if they don't exist # TODO: maybe use mtime / stat as a shortcut for hashing below # TODO: strip trailing whitespace? # TODO: save atomically? if self.file.filename is None: filename = self.prompt('enter filename') if filename is PromptResult.CANCELLED: return PromptResult.CANCELLED else: self.file.filename = filename if os.path.isfile(self.file.filename): with open(self.file.filename, encoding='UTF-8', newline='') as f: *_, sha256 = get_lines(f) else: sha256 = hashlib.sha256(b'').hexdigest() contents = self.file.nl.join(self.file.buf) sha256_to_save = hashlib.sha256(contents.encode()).hexdigest() # the file on disk is the same as when we opened it if sha256 not in (self.file.sha256, sha256_to_save): self.status.update('(file changed on disk, not implemented)') return PromptResult.CANCELLED try: with open( self.file.filename, 'w', encoding='UTF-8', newline='', ) as f: f.write(contents) except OSError as e: self.status.update(f'cannot save file: {e}') return PromptResult.CANCELLED self.file.modified = False self.file.sha256 = sha256_to_save num_lines = len(self.file.buf) - 1 lines = 'lines' if num_lines != 1 else 'line' self.status.update(f'saved! ({num_lines} {lines} written)') # fix up modified state in undo / redo stacks for stack in (self.file.undo_stack, self.file.redo_stack): first = True for action in reversed(stack): action.end_modified = not first action.start_modified = True first = False return None
def test_get_lines_sha256_checksum(): ret = get_lines(io.StringIO('')) sha256 = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' assert ret == ([''], '\n', False, sha256)
def test_get_lines(s, lines, nl, mixed): # sha256 tested below ret_lines, ret_nl, ret_mixed, _ = get_lines(io.StringIO(s)) assert (ret_lines, ret_nl, ret_mixed) == (lines, nl, mixed)