def test_overwrite_multiple_progress_bars_with_section_outputs(ansi_io): output1 = ansi_io.section() output2 = ansi_io.section() bar1 = ProgressBar(output1, 50, 0) bar2 = ProgressBar(output2, 50, 0) bar1.start() bar2.start() bar2.advance() bar1.advance() output = [ " 0/50 [>---------------------------] 0%", " 0/50 [>---------------------------] 0%", "\x1b[1A\x1b[0J 1/50 [>---------------------------] 2%", "\x1b[2A\x1b[0J 1/50 [>---------------------------] 2%", "\x1b[1A\x1b[0J 1/50 [>---------------------------] 2%", " 1/50 [>---------------------------] 2%", ] expected = "\n".join(output) + "\n" assert expected == ansi_io.fetch_output()
def test_non_decorated_output(io): bar = ProgressBar(io, 200, 0) bar.start() for i in range(200): bar.advance() bar.finish() expected = "\n".join( [ " 0/200 [>---------------------------] 0%", " 20/200 [==>-------------------------] 10%", " 40/200 [=====>----------------------] 20%", " 60/200 [========>-------------------] 30%", " 80/200 [===========>----------------] 40%", " 100/200 [==============>-------------] 50%", " 120/200 [================>-----------] 60%", " 140/200 [===================>--------] 70%", " 160/200 [======================>-----] 80%", " 180/200 [=========================>--] 90%", " 200/200 [============================] 100%", ] ) assert expected == io.fetch_error()
def _download_archive(self, operation: Install | Update, link: Link) -> Path: response = self._authenticator.request("get", link.url, stream=True, io=self._sections.get( id(operation), self._io)) wheel_size = response.headers.get("content-length") operation_message = self.get_operation_message(operation) message = ( f" <fg=blue;options=bold>•</> {operation_message}: <info>Downloading...</>" ) progress = None if self.supports_fancy_output(): if wheel_size is None: self._write(operation, message) else: from cleo.ui.progress_bar import ProgressBar progress = ProgressBar(self._sections[id(operation)], max=int(wheel_size)) progress.set_format(message + " <b>%percent%%</b>") if progress: with self._lock: self._sections[id(operation)].clear() progress.start() done = 0 archive: Path = self._chef.get_cache_directory_for_link( link) / link.filename archive.parent.mkdir(parents=True, exist_ok=True) with archive.open("wb") as f: for chunk in response.iter_content(chunk_size=4096): if not chunk: break done += len(chunk) if progress: with self._lock: progress.set_progress(done) f.write(chunk) if progress: with self._lock: progress.finish() return archive
def test_clear(ansi_io): bar = ProgressBar(ansi_io, 50, 0) bar.start() bar.set_progress(25) bar.clear() output = [ " 0/50 [>---------------------------] 0%", " 25/50 [==============>-------------] 50%", "", ] expected = generate_output(output) assert expected == ansi_io.fetch_error()
def test_customizations(ansi_io): bar = ProgressBar(ansi_io, 10, 0) bar.set_bar_width(10) bar.set_bar_character("_") bar.set_empty_bar_character(" ") bar.set_progress_character("/") bar.set_format(" %current%/%max% [%bar%] %percent:3s%%") bar.start() bar.advance() output = [" 0/10 [/ ] 0%", " 1/10 [_/ ] 10%"] expected = generate_output(output) assert expected == ansi_io.fetch_error()
def test_overwrite_with_section_output(ansi_io): bar = ProgressBar(ansi_io.section(), 50, 0) bar.start() bar.display() bar.advance() bar.advance() output = [ " 0/50 [>---------------------------] 0%", " 1/50 [>---------------------------] 2%", " 2/50 [=>--------------------------] 4%", ] expected = "\n\x1b[1A\x1b[0J".join(output) + "\n" assert expected == ansi_io.fetch_output()
def test_percent_not_hundred_before_complete(ansi_io): bar = ProgressBar(ansi_io, 200, 0) bar.start() bar.display() bar.advance(199) bar.advance() output = [ " 0/200 [>---------------------------] 0%", " 199/200 [===========================>] 99%", " 200/200 [============================] 100%", ] expected = generate_output(output) assert expected == ansi_io.fetch_error()
def test_percent(ansi_io): bar = ProgressBar(ansi_io, 50, 0) bar.start() bar.display() bar.advance() bar.advance() output = [ " 0/50 [>---------------------------] 0%", " 1/50 [>---------------------------] 2%", " 2/50 [=>--------------------------] 4%", ] expected = generate_output(output) assert expected == ansi_io.fetch_error()
def test_set_current_progress(ansi_io): bar = ProgressBar(ansi_io, 50, 0) bar.start() bar.display() bar.advance() bar.set_progress(15) bar.set_progress(25) output = [ " 0/50 [>---------------------------] 0%", " 1/50 [>---------------------------] 2%", " 15/50 [========>-------------------] 30%", " 25/50 [==============>-------------] 50%", ] expected = generate_output(output) assert expected == ansi_io.fetch_error()
def test_multiline_format(ansi_io): bar = ProgressBar(ansi_io, 3, 0) bar.set_format("%bar%\nfoobar") bar.start() bar.advance() bar.clear() bar.finish() output = [ ">---------------------------\nfoobar", "=========>------------------\nfoobar", "\n", "============================\nfoobar", ] expected = generate_output(output) assert expected == ansi_io.fetch_error()
def test_overwrite_with_shorter_line(ansi_io): bar = ProgressBar(ansi_io, 50, 0) bar.set_format(" %current%/%max% [%bar%] %percent:3s%%") bar.start() bar.display() bar.advance() # Set shorter format bar.set_format(" %current%/%max% [%bar%]") bar.advance() output = [ " 0/50 [>---------------------------] 0%", " 1/50 [>---------------------------] 2%", " 2/50 [=>--------------------------]", ] expected = generate_output(output) assert expected == ansi_io.fetch_error()
def test_format(ansi_io): output = [ " 0/10 [>---------------------------] 0%", " 10/10 [============================] 100%", ] expected = generate_output(output) # max in construct, no format ansi_io.clear_error() bar = ProgressBar(ansi_io, 10) bar.start() bar.advance(10) bar.finish() assert expected == ansi_io.fetch_error() # max in start, no format ansi_io.clear_error() bar = ProgressBar(ansi_io) bar.start(10) bar.advance(10) bar.finish() assert expected == ansi_io.fetch_error() # max in construct, explicit format before ansi_io.clear_error() bar = ProgressBar(ansi_io, 10) bar.set_format("normal") bar.start() bar.advance(10) bar.finish() assert expected == ansi_io.fetch_error() # max in start, explicit format before ansi_io.clear_error() bar = ProgressBar(ansi_io) bar.set_format("normal") bar.start(10) bar.advance(10) bar.finish() assert expected == ansi_io.fetch_error()
def _upload_file( self, session: requests.Session, url: str, file: Path, dry_run: Optional[bool] = False, ) -> requests.Response: from cleo.ui.progress_bar import ProgressBar data = self.post_data(file) data.update({ # action ":action": "file_upload", "protocol_version": "1", }) data_to_send = self._prepare_data(data) with file.open("rb") as fp: data_to_send.append( ("content", (file.name, fp, "application/octet-stream"))) encoder = MultipartEncoder(data_to_send) bar = ProgressBar(self._io, max=encoder.len) bar.set_format( f" - Uploading <c1>{file.name}</c1> <b>%percent%%</b>") monitor = MultipartEncoderMonitor( encoder, lambda monitor: bar.set_progress(monitor.bytes_read)) bar.start() resp = None try: if not dry_run: resp = session.post( url, data=monitor, allow_redirects=False, headers={"Content-Type": monitor.content_type}, ) if dry_run or 200 <= resp.status_code < 300: bar.set_format( f" - Uploading <c1>{file.name}</c1> <fg=green>%percent%%</>" ) bar.finish() elif resp.status_code == 301: if self._io.output.is_decorated(): self._io.overwrite( f" - Uploading <c1>{file.name}</c1> <error>FAILED</>" ) raise UploadError("Redirects are not supported. " "Is the URL missing a trailing slash?") except (requests.ConnectionError, requests.HTTPError) as e: if self._io.output.is_decorated(): self._io.overwrite( f" - Uploading <c1>{file.name}</c1> <error>FAILED</>") raise UploadError(e) finally: self._io.write_line("") return resp
def _upload_file( self, session: requests.Session, url: str, file: Path, dry_run: bool = False, skip_existing: bool = False, ) -> None: from cleo.ui.progress_bar import ProgressBar data = self.post_data(file) data.update({ # action ":action": "file_upload", "protocol_version": "1", }) data_to_send: list[tuple[str, Any]] = self._prepare_data(data) with file.open("rb") as fp: data_to_send.append( ("content", (file.name, fp, "application/octet-stream"))) encoder = MultipartEncoder(data_to_send) bar = ProgressBar(self._io, max=encoder.len) bar.set_format( f" - Uploading <c1>{file.name}</c1> <b>%percent%%</b>") monitor = MultipartEncoderMonitor( encoder, lambda monitor: bar.set_progress(monitor.bytes_read)) bar.start() resp = None try: if not dry_run: resp = session.post( url, data=monitor, allow_redirects=False, headers={"Content-Type": monitor.content_type}, timeout=REQUESTS_TIMEOUT, ) if resp is None or 200 <= resp.status_code < 300: bar.set_format( f" - Uploading <c1>{file.name}</c1> <fg=green>%percent%%</>" ) bar.finish() elif resp.status_code == 301: if self._io.output.is_decorated(): self._io.overwrite( f" - Uploading <c1>{file.name}</c1> <error>FAILED</>" ) raise UploadError("Redirects are not supported. " "Is the URL missing a trailing slash?") elif resp.status_code == 400 and "was ever registered" in resp.text: self._register(session, url) resp.raise_for_status() elif skip_existing and self._is_file_exists_error(resp): bar.set_format( f" - Uploading <c1>{file.name}</c1> <warning>File exists." " Skipping</>") bar.display() else: resp.raise_for_status() except (requests.ConnectionError, requests.HTTPError) as e: if self._io.output.is_decorated(): self._io.overwrite( f" - Uploading <c1>{file.name}</c1> <error>FAILED</>") raise UploadError(e) finally: self._io.write_line("")