Package: alive-progress
alive-progressHave you ever wondered where your lengthy processing was at, and when would it finish? Do you usually hitRETURNseveral times to make sure it didn't crash, or the SSH connection didn't freeze? Have you ever thought it'd be awesome to be able topause some processingwithout hassle, return to the Python prompt to manually fix some items, thenseamlessly resumeit? I did...I've started this new progress bar thinking about all that, behold thealive-progress! 😃Introducing the newest concept in progress bars for Python!alive-progressis in a class of its own, with an array of cool features that set it apart. Here are a few highlights:A mesmerizinglive spinnerthat clearly shows your lengthy process did not crash and your SSH connection did not freeze, withvisual feedbackreacting to your processing speed.An efficientmultithreadedbar that updates itself at a fraction of the actual processing speed to keepCPU usage lowand avoid terminal spamming (1,000,000 iterations per second equates to roughly 60 updates per second, and you can also calibrate this to your liking).AnETA(expected time of arrival) feature with an intelligentExponential Smoothing Algorithmthat shows the time to completion, allowing you to plan your time and manage your workload more effectively.Automaticprintandlogginghooks that provide seamless integration and effortless tracking, even enriching them with the current bar position when they occur.It prints anice receiptwhen the processing finishes, including the elapsed time and the observed throughput.It detectsunderandoverflows, enabling you to track hits, misses, or any desired count, not necessarily the actual iterations.You canpauseit! That's right, you heard it here first! No other progress bar anywhere has this feature! You can get back to the Python prompt during any processing, adjust some items, and get back into that running process as if it had never stopped! Allalive_barwidgets are kept as they were, and the elapsed time nicely ignores the paused time!It ishighly customizable, with a smorgasbord of spinner and bar styles, as well as several ready-to-use factories to easily generate yours! You can even use the super powerful and coolcheck()tool to help you design your own animations! You can see all the generated frames and cycles exploded on screen, with several verbosity levels, even including analiverendition! It's boundless creativity at your fingertips!Table of contentsThis README is always evolving, so do take a more comprehensive look from time to time... You might find great new details in other sections! 😊alive-progressTable of contents📌 NEW in 3.1 seriesUsingalive-progressGet itTry itAwake itMaster itDisplaying messagesAuto-iteratingModes of operationDefinite/unknown: CountersManual: PercentagesSummary of ModesThebar()handlersStylesConfigurationCreate your own animationsIntro: How do they work?A Spinner Compiler, really?Spinner FactoriesBar FactoriesAdvancedThe Pause MechanismLoop-less useFPS CalibrationForcing animations on PyCharm, Jupyter, etc.Interesting factsTo doPython End of Life noticeFor new Python 2.7 and 3.5For new Python 3.6License📌 NEW in 3.1 seriesA very cool update here! In addition to polishing things up and improving terminal support, nowalive-progresssupports resuming computations!When processing huge datasets or things that take a long time, you might either use batches or cache partial results. Then, in case it stops and is restarted, you end up skipping all those already done items very quickly, which makes thealive_barthink you're processing thousands of items per second, which in turn completely ruins the ETA... But not anymore! Just tellbar()that you've skipped items... 👏You can use it in two ways:1.If you do know where you've stopped:withalive_bar(120000)asbar:bar(60000,skipped=True)foriinrange(60000,120000):# process itembar()Yep, just callbar(N, skipped=True)once, with the number of items.2.If you do not know or the items are scattered:withalive_bar(120000)asbar:foriinrange(120000):ifdone(i):bar(skipped=True)continue# process itembar()Yep, it's as simple as that! Just callbar(skipped=True)when an item is already done, orbar()as usual otherwise. You could also share a singlebar(skipped=?)call at the end, with a bool saying whether you did skip that item or not. Cool, huh?Also in this version:newmax_colsconfig setting, the number of columns to use if not possible to fetch it, like in jupyter and other platforms which doesn't support sizefix fetching the size of the terminal when using stderrofficially supports Python 3.11includedrufflinter before building📌 NEW in 3.0 seriesYep, I could finally get this version out! These are the new goodies:Units support! You can now label the data you're processing, likeB,bytes, or even°C!Automatic scaling! With support for SI (base 1000), IEC (base 1024), and even an alternate SI with base 1024, you'll be well served!Configurable precision! When your numbers are scaled, you get to choose how many decimals they display!Automatic stats scaling for slow throughputs! If your processing takes minutes or more, now you'll see rates per minute, per hour, and even per day! (It works within the auto-scaling system!)Support for usingsys.stderrand other files instead ofsys.stdout!Smoothed out the rate estimation with the same Exponential Smoothing Algorithm that powers the ETA, so the bar returns a more realistic ETA!Query the currently running widgets' data, like the monitor, rate, and ETA!New help system on configuration errors, which explains why a value was not accepted, and what were the expected ones!Highly anticipated fixes:Support for reusing logging handlers! No moreTypeError: unhashable type: 'types.SimpleNamespace'.Support for logging when usingRotatingFileHandlers! Yep, seek support is here.Fix unknown mode always ending with a warning (!)And last but not least, a more polished layout for you to enjoy your progress!📌 NEW in 2.4 seriesNow,alive_barsupportsDual Linetext mode!If you ever wanted to include longer situational messages within the bar, you probably felt squeezed into one line. You had to shrink the beautifully animated bar or, even worse, remove widgets (!) to be able to see what you needed...Not anymore!! You can now make the barDual Line, and put text below it!Yes, there's a message below the whole bar, and any other print/logging messages scroll above it!letters=[chr(ord('A')+x)forxinrange(26)]withalive_bar(26,dual_line=True,title='Alphabet')asbar:forcinletters:bar.text=f'-> Teaching the letter:{c}, please wait...'ifcin'HKWZ':print(f'fail "{c}", retry later')time.sleep(0.3)bar()on7:fail"H",retrylateron10:fail"K",retrylaterAlphabet|███████████████████████████▊|▃▅▇18/26[69%]in6s(3.2/s,eta:3s)->Teachingtheletter:S,pleasewait...There's also a newfinalizefunction parameter inalive_itwhich enables you to set the title and/or text of the final receipt, and improved logging support which detects customized loggers.📌 NEW in 2.3 seriesThis is all about customization; the core widgets can now be changed:send a string to themonitor,elapsed, andstatswidgets to make them look anyway you want!It's incredible that these strings support all Python format features, so you can e.g.,{percent:.1%}😉.They can be further customized when on thefinal receipt!newmonitor_end,elapsed_end, andstats_end, with dynamic formats inherited from the standard ones!If you've hidden some widgets before, just so they wouldn't appear on the receipt, now you can see them in all their running glory, and hide just the receipt ones! Or the other way around 😜Another addition, nowalive-progressbeautifully renders its cool final receipt whenever it is stopped, even if you CTRL+C it prematurely! I don't know why I haven't thought about that before...Download|██████████████████⚠︎|(!)45/100[45%]in4.8s(9.43/s)And finally, you can choose to disable CTRL+C at all! The default is the saferctrl_c=True, which does make CTRL-C work as usual.Disable itctrl_c=False, to make your interactivealive_barmuch smoother to use (there are no stack traces if you stop it), and/or if it is at the top-level of your program!Beware: If it is e.g. inside a for-loop, it will just continue to the next iteration, which may or may not be what you want...foriinrange(10):withalive_bar(100,ctrl_c=False,title=f'Download{i}')asbar:foriinrange(100):time.sleep(0.02)bar()Download0|████████▊⚠︎|(!)22/100[22%]in0.6s(36.40/s)Download1|████████████████▊⚠︎|(!)42/100[42%]in1.0s(41.43/s)Download2|██████▍⚠︎|(!)16/100[16%]in0.4s(39.29/s)Download3|█████▋⚠︎|(!)14/100[14%]in0.4s(33.68/s)Download4|█████████████▎⚠︎|(!)33/100[33%]in0.8s(39.48/s)Download5|███████▎⚠︎|(!)18/100[18%]in0.5s(37.69/s)Download6|█████▎⚠︎|(!)13/100[13%]in0.3s(37.28/s)Download7|████████████⚠︎|(!)30/100[30%]in0.8s(38.43/s)Download8|██████⚠︎|(!)15/100[15%]in0.4s(36.26/s)...📌 NEW in 2.2 seriesSome major new features, often requested, have finally landed!bar title can be dynamically set, changed, or even removed after being displayednew custom fps system, which enables very slow refresh rates (to let it run on those k8s for long periods)the final receipt can be totally hidden (great for special effects, like using the cool spinners standalone)new support forclick.echo()printingterminal columns detection is safer for exotic environmentsrequires Python 3.7+📌 NEW in 2.1 seriesYES! Nowalive-progresshas support for Jupyter Notebooks and also includes aDisabledstate! Both were highly sought after, and have finally landed!And better, I've implemented an auto-detection mechanism for jupyter notebooks, so it just works, out of the box, without any changes in your code!!See for yourself:It seems to work very well, but at this moment, it should be consideredexperimental.There were instances in which some visual glitches have appeared, like twoalive_barrefreshes being concatenated together instead of over one another... And it's something I think I can't possibly work around: it seems Jupyter sometimes refresh the canvas at odd times, which makes it lose some data. Please let me know on the issues if something funnier arises.📌 NEW in 2.0 seriesThis is a major breakthrough inalive-progress!I took 1 year developing it, and I'm very proud of what I've accomplished \o/now, there's complete support for Emojis 🤩 and exotic Unicode chars in general, which required MAJOR refactoring deep within the project, giving rise to what I called "Cell Architecture" => now, all internal components use and generate streams of cells instead of characters, and correctly interpret grapheme clusters — those so-called wide chars, which are encoded with a variable number of chars, but always take two cells on screen!! This has enabled us to render complex multi-chars symbols as if they were one, thus making them work on any spinners, bars, texts, borders and backgrounds, even when fractured!!! Pretty advanced stuff 🤓new super cool spinner compiler and runner, which generates complete animations ahead of time, and plays these ready-to-go animations seamlessly, with no overhead at all! 🚀the spinner compiler also includes advanced extra commands to generate and modify animations, like reshape, replace, transpose, or randomize the animation cycles!new powerful and polished.check()tools that compile and beautifully render all frames from all animation cycles of spinners and bars! they can even include complete frame data, internal codepoints, and even their animations! 👏bars engine revamp, with invisible fills, advanced support for multi-char tips (which gradually enter and leave the bar), borders, tips and errors of any length, and underflow errors that can leap into the border if they can't fit!spinners engine revamp, with standardized factory signatures, improved performance, new types, and new features: smoother bouncing spinners (with an additional frame at the edges), optimized scrolling of text messages (which go slower and pause for a moment at the edges), new alongside and sequential spinners, nicer effect in alongside spinners (which use weighted spreading over the available space), smoother animation in scrolling spinners (when the input is longer than the available space)new builtin spinners, bars, and themes, which make use of the new animation featuresnew showtime that displays themes and is dynamic => it does not scroll the screen when it can't fit vertically or horizontally, and can even filter for patterns!improved support for logging into files, which gets enriched as the print hook is!several new configuration options for customizing appearance, including support for disabling anyalive-progresswidgets!includes a new iterator adapter,alive_it, that accepts an iterable and callsbar()for you!requires Python 3.6+ (and officially supports Python 3.9 and 3.10)Since this is a major version change, direct backward compatibility is not guaranteed. If something does not work at first, just check the new imports and functions' signatures, and you should be good to go. All previous features should still work here! 👍Usingalive-progressGet itJust install with pip:❯pipinstallalive-progressTry itIf you're wondering what styles are builtin, it'sshowtime! ;)fromalive_progress.stylesimportshowtimeshowtime()Note: Please disregard the path in the animated gif below, the correct one is above. These long gifs are very time-consuming to generate, so I can't make another on every single change. Thanks for your understanding.I've made these styles just to try all the animation factories I've created, but I think some of them ended up very, very cool! Use them at will, and mix them to your heart's content!Do you want to see actualalive-progressbars gloriously running in your system before trying them yourself?❯python-malive_progress.tools.demoAwake itCool, huh?? Now enter anipythonREPL and try this:fromalive_progressimportalive_barimporttimeforxin1000,1500,700,0:withalive_bar(x)asbar:foriinrange(1000):time.sleep(.005)bar()You'll see something like this, with cool animations throughout the process 😜:|████████████████████████████████████████| 1000/1000 [100%] in 5.8s (171.62/s)
|██████████████████████████▋⚠︎            | (!) 1000/1500 [67%] in 5.8s (172.62/s)
|████████████████████████████████████████✗︎ (!) 1000/700 [143%] in 5.8s (172.06/s)
|████████████████████████████████████████| 1000 in 5.8s (172.45/s)Nice, huh? Loved it? I knew you would, thank you 😊.To actually use it, just wrap your normal loop in analive_barcontext manager like this:withalive_bar(total)asbar:# declare your expected totalforiteminitems:# <<-- your original loopprint(item)# process each itembar()# call `bar()` at the endAnd it's alive! 👏So, in short: retrieve the items as always, enter thealive_barcontext manager with the number of items, and then iterate/process those items, callingbar()at the end! It's that simple! :)Master ititemscan be any iterable, like for example, a queryset;the first argument of thealive_baris the expected total, likeqs.count()for querysets,len(items)for iterables with length, or even a static number;the callbar()is what makes the bar go forward — you usually call it in every iteration, just after finishing an item;if you callbar()too much (or too few at the end), the bar will graphically render that deviation from the expectedtotal, making it very easy to notice overflows and underflows;to retrieve the current bar count or percentage, callbar.current.You can get creative! Since the bar only goes forward when you callbar(), it isindependent of the loop! So you can use it to monitor anything you want, like pending transactions, broken items, etc., or even call it more than once in the same iteration! So, in the end, you'll get to know how many of those "special" events there were, including their percentage relative to the total!Displaying messagesWhile inside analive_barcontext, you can effortlessly display messages tightly integrated with the current progress bar being displayed! It won't break in any way and will even enrich your message!the coolbar.text('message')andbar.text = 'message'set a situational message right within the bar, where you can display something about the current item or the phase the processing is in;the (📌 new) dynamic title, which can be set right at the start, but also be changed anytime withbar.title('Title')andbar.title = 'Title'— mix withtitle_lengthto keep the bar from changing its length;the usual Pythonprint()statement, wherealive_barnicely cleans up the line, prints your message alongside the current bar position at the time, and continues the bar right below it;the standard Pythonloggingframework, including file outputs, is also enriched exactly like the previous one;if you're using click CLI lib, you can even useclick.echo()to print styled text.Awesome right? And all of these work just the same in a terminal or in a Jupyter notebook!Auto-iteratingYou now have a quicker way to monitor anything! Here, the items are automatically tracked for you!Behold thealive_it=> thealive_bariterator adapter!Simply wrap your items with it, and loop over them as usual!The bar will just work; it's that simple!fromalive_progressimportalive_itforiteminalive_it(items):# <<-- wrapped itemsprint(item)# process each itemHOW COOL IS THAT?! 😜Allalive_barparameters apply buttotal, which is smarter (if not supplied, it will be auto-inferred from your data usinglenorlength_hint), andmanualthat does not make sense here.Note there isn't anybarhandle at all in there. But what if you do want it, e.g. to set text messages or retrieve the current progress?You can interact with the internalalive_barby just assigningalive_itto a variable like this:bar=alive_it(items)# <<-- bar with wrapped itemsforiteminbar:# <<-- iterate on barprint(item)# process each itembar.text(f'ok:{item}')# WOW, it works!Note that this is a slightly specialbar, which does not supportbar(), since the iterator adapter tracks items automatically for you. Also, it supportsfinalize, which enables you to set the title and/or text of the final receipt:alive_it(items,finalize=lambdabar:bar.text('Success!'))...In a nutshell:full use is alwayswith alive_bar() as bar, where you iterate and callbar()whenever you want;quick adapter use isfor item in alive_it(items), where items are automatically tracked;full adapter use isbar = alive_it(items), where in addition to items being automatically tracked, you get a special iterablebarable to customize the inneralive_progresshowever you want.Modes of operationDefinite/unknown: CountersActually, thetotalargument is optional. If you do provide it, the bar enters indefinite mode, the one used for well-bounded tasks. This mode has all the widgetsalive-progresshas to offer: progress, count, throughput, and ETA.If you don't, the bar enters inunknown mode, the one used for unbounded tasks. In this mode, the whole progress bar is animated, as it's not possible to determine the progress, and therefore the ETA. But you still get the count and throughput widgets as usual.The cool spinner is still present here alongside the progress bar, both running their animations concurrently and independently of each other, rendering a unique show in your terminal! 😜Both definite and unknown modes use internally acounterto maintain progress. This is the source value which all widgets are derived from.Manual: PercentagesOn the other hand, themanual modeuses internally apercentageto maintain progress. This enables you to get complete control of the bar position! It's usually used to monitor processes that only feed you the percentage of completion, or to generate some kind of special effects.To use it, just include amanual=Trueargument intoalive_bar(orconfig_handler), and you get to send any percentage to thebar()handler! For example, to set it to 15%, just callbar(0.15)— which is 15 / 100.You can also usetotalhere! If you do provide it,alive-progresswill infer an internalcounterby itself, and thus will be able to offer you the same count, throughput, and ETA widgets!If you don't, you'll at least get rough versions of the throughput and ETA widgets. The throughput will use "%/s" (percent per second), and the ETA will be till 1.0 (100%). Both are very inaccurate but better than nothing.You can callbarin manual mode as frequently as you want! The refresh rate will still be asynchronously computed as usual, according to the current progress and the elapsed time, so you won't ever spam the terminal with more updates than it can handle.Summary of ModesWhentotalis provided all is cool:modecounterpercentagethroughputETAover/underflowdefinite✅ (user tick)✅ (inferred)✅✅✅manual✅ (inferred)✅ (user set)✅✅✅When it isn't, some compromises have to be made:modecounterpercentagethroughputETAover/underflowunknown✅ (user tick)❌✅❌❌manual❌✅ (user set)⚠️ (simpler)⚠️ (rough)✅But actually it's quite simple, you do not need to think about which mode you should use:Just always send thetotalif you have it, and usemanualif you need it!It will just work the best it can! 👏 \o/Thebar()handlersThebar()handlers support either relative or absolute semantics, depending on the mode:definiteandunknownmodes userelative positioning, so you can just callbar()to increment the counter by one, or send any other positive increment likebar(5)to increment by those at once;manualmodes useabsolute positioning, so you can just callbar(0.35)to instantly put the bar in 35% position — this argument is mandatory here!The manual modes enable you to get super creative! Since you can set the bar instantly to whatever position you want, you could:make it go backwards — perhaps to graphically display the timeout of something;create special effects — perhaps to act as a real-time gauge of some sort.In any case, to retrieve the current count/percentage, just call:bar.current:indefiniteandunknownmodes, this provides aninteger— the actual internal counter;inmanualmodes, this provides afloatin the interval [0, 1] — the last percentage set.Last but not least, thebar()handler of thedefinitemode has a unique ability: skipping items for an accurate ETA! Just callbar(skipped=False)orbar(skipped=True)to use it. When skipped is True, that item(s) are ignored when computing the rate, and thus not ruining the ETA.Maintaining an open source project is hard and time-consuming, and I've put much ❤️ and effort into this.If you've appreciated my work, you can back me up with a donation! Thank you 😊StylesTheshowtimeexhibit has an optional argument to choose which show to present,Show.SPINNERS(default),Show.BARSorShow.THEMES, do take a look at them! ;)fromalive_progress.stylesimportshowtime,ShowNote: Please disregard the path in the animated gif below, the correct one is above. These long gifs are very time-consuming to generate, so I can't make another on every single change. Thanks for your understanding.And the themes one (📌 new in 2.0):Theshowtimeexhibit also accepts some customization options:fps: the frames per second rate refresh rate, default is 15;length: the length of the bars, default is 40;pattern: a filter to choose which ones to display.For example to get a marine show, you canshowtime(pattern='boat|fish|crab'):You can also access these shows with the shorthandsshow_bars(),show_spinners(), andshow_themes()!There's also a small utility calledprint_chars(), to help find that cool character to put in your customized spinners and bars, or to determine if your terminal does support Unicode characters.ConfigurationThere are several options to customize both appearance and behavior!All of them can be set both directly in thealive_baror globally in theconfig_handler!These are the options - default values in brackets:title: an optional, always visible bar titlelength: [40] the number of cols to render the animated progress barmax_cols: [80] the maximum cols to use if not possible to fetch it, like in jupyterspinner: the spinner style to be rendered next to the bar↳ accepts a predefined spinner name, a custom spinner factory, or Nonebar: the bar style to be rendered in known modes↳ accepts a predefined bar name, a custom bar factory, or Noneunknown: the bar style to be rendered in the unknown mode↳ accepts a predefined spinner name, or a custom spinner factory (cannot be None)theme: ['smooth'] a set of matching spinner, bar, and unknown↳ accepts a predefined theme nameforce_tty: [None] forces animations to be on, off, or according to the tty (more detailshere)↳ None -> auto select, according to the terminal/Jupyter↳ True -> unconditionally enables animations, but still auto-detects Jupyter Notebooks↳ False -> unconditionally disables animations, keeping only the final receiptfile: [sys.stdout] the file object to use:sys.stdout,sys.stderr, or a similarTextIOWrapperdisable: [False] if True, completely disables all output, do not install hooksmanual: [False] set to manually control the bar positionenrich_print: [True] enriches print() and logging messages with the bar positionreceipt: [True] prints the nice final receipt, disables if Falsereceipt_text: [False] set to repeat the last text message in the final receiptmonitor(bool|str): [True] configures the monitor widget152/200 [76%]↳ send a string with{count},{total}and{percent}to customize itelapsed(bool|str): [True] configures the elapsed time widgetin 12s↳ send a string with{elapsed}to customize itstats(bool|str): [True] configures the stats widget(123.4/s, eta: 12s)↳ send a string with{rate}and{eta}to customize itmonitor_end(bool|str): [True] configures the monitor widget within final receipt↳ same as monitor, the default format is dynamic, it inheritsmonitor's oneelapsed_end(bool|str): [True] configures the elapsed time widget within final receipt↳ same as elapsed, the default format is dynamic, it inheritselapsed's onestats_end(bool|str): [True] configures the stats widget within final receipt↳ send a string with{rate}to customize it (no relation to stats)title_length: [0] fixes the length of titles, or 0 for unlimited↳ title will be truncated if longer, and a cool ellipsis "…" will appear at the endspinner_length: [0] forces the spinner length, or0for its natural onerefresh_secs: [0] forces the refresh period to this,0is the reactive visual feedbackctrl_c: [True] if False, disables CTRL+C (captures it)dual_line: [False] if True, places the text below the barunit: any text that labels your entitiesscale: the scaling to apply to units:None,SI,IEC, orSI2↳ supports aliases:Falseor''->None,True->SI,10or'10'->SI,2or'2'->IECprecision: [1] how many decimals do display when scalingAnd there's also one that can only be set locally in analive_barcontext:calibrate: maximum theoretical throughput to calibrate the animation speed (more detailshere)To set them locally, just send them as keyword arguments toalive_bar:withalive_bar(total,title='Processing',length=20,bar='halloween')asbar:...To use them globally, send them toconfig_handler, and anyalive_barcreated after that will include those options! And you can mix and match them, local options always have precedence over global ones:fromalive_progressimportconfig_handlerconfig_handler.set_global(length=20,spinner='wait')withalive_bar(total,bar='blocks',spinner='twirls')asbar:# the length is 20, the bar is 'blocks' and the spinner is 'twirls'....Create your own animationsYes, you can assemble your own spinners! And it's easy!I've created a plethora of special effects, so you can just mix and match them any way you want! There are frames, scrolling, bouncing, sequential, alongside, and delayed spinners! Get creative! 😍Intro: How do they work?The spinners' animations are engineered by very advanced generator expressions, deep within several layers of meta factories, factories and generators 🤯!the meta factory (public interface) receives the styling parameters from you, the user, and processes/stores them inside a closure to create the actual factory => this is the object you'll send to bothalive_barandconfig_handler;internally it still receives other operating parameters (like for instance the rendition length), to assemble the actual generator expression of the animation cycles of some effect, within yet another closure;this, for each cycle, assembles another generator expression for the animation frames of the same effect;these generators together finally produce the streams of cycles and frames of the cool animations we see on the screen! Wow! 😜👏These generators are capable of multiple different animation cycles according to the spinner behavior, e.g. a bouncing spinner can run one cycle to smoothly bring a subject into the scene, then repeatedly reposition it until the other side, then make it smoothly disappear off the scene => and this is all only one cycle! Then it can be followed by another cycle to make it all again but backwards!
And bouncing spinners also acceptdifferentandalternatingpatterns in both the right and left directions, which makes them generate the cartesian product of all the combinations, possibly producing dozens of different cycles until they start repeating them!! 🤯And there's more, I think one of the most impressive achievements I got in this animation system (besides the spinner compiler itself)... They only yield more animation frames until the current cycle is not exhausted, thenthey halt themselves! Yep, the next cycle does not start just yet! This behavior creates natural breaks in exactly the correct spots, where the animations are not disrupted, so I can smoothly link with whatever other animation I want!!This has all kinds of cool implications: the cycles can have different frame counts, different screen lengths, they do not need to be synchronized, they can create long different sequences by themselves, they can cooperate to play cycles in sequence or alongside, and I can amaze you displaying several totally distinct animations at the same time without any interferences whatsoever!It's almost like they were...alive!! 😄==> Yes, that's where this project's name came from! 😉A Spinner Compiler, really?Now, these generators of cycles and frames are fully consumed ahead of time by theSpinner Compiler! This is a very cool new processor that I made inside theCell Architectureeffort, to make all these animations work even in the presence of wide chars or complex grapheme clusters! It was very hard to make these clusters  gradually enter and exit frames, smoothly, while keeping them from breaking the Unicode encoding and especially maintain their original lengths in all frames! Yes, several chars in sequence can represent another completely different symbol, so they cannot ever be split! They have to enter and exit the frame always together, all at once, or the grapheme won't show up at all (an Emoji for instance)!! Enter theSpinner Compiler......This has made possible some incredible things!! Since this Compiler generates the whole spinner frame data beforehand:the grapheme fixes can be applied only once;the animations do not need to be calculated again!So, I can just collect all thatready to playanimations and be done with it,no runtime overheadat all!! 👏Also, with the complete frame data compiled and persisted, I could create several commands torefactorthat data, like changing shapes, replacing chars, adding visual pauses (frame repetitions), generating bouncing effects on-demand over any content, and even transposing cycles with frames!!But how can you see these effects? Does the effect you created look good? Or is it not working as you thought? YES, now you can see all generated cycles and frames analytically, in a very beautiful rendition!!I love what I've achieved here 😊, it's probably THE most beautiful tool I've ever created... Behold thechecktool!!It's awesome if I say so myself, isn't it? And a very complex piece of software I'm proud of,take a look at its codeif you'd like.And thechecktool is much more powerful! For instance, you can see the codepoints of the frames!!! And maybe have a glimpse of why this version was so, so very hard and complex to make...In red, you see the grapheme clusters, that occupy one or two "logical positions", regardless of their actual sizes... These are the "Cells" of the newCell Architecture...Look how awesome an Emoji Flag is represented:The flag seems to move so smoothly because it uses "half-characters"! Since it is a wide char,alive-progressknows it will be rendered with "two visible chars", and the animations consider this, but compose with spaces, which occupy only one. When one uses mixed backgrounds, the situation is much more complex...Spinner FactoriesThe types of factories I've created are:frames: draws any sequence of characters at will, that will be played frame by frame in sequence;scrolling: generates a smooth flow from one side to the other, hiding behind or wrapping upon invisible borders — allows using subjects one at a time, generating several cycles of distinct characters;bouncing: similar toscrolling, but makes the animations bounce back to the start, hiding behind or immediately bouncing upon invisible borders;sequentialget a handful of factories and play them one after the other sequentially! allows to intermix them or not;alongsideget a handful of factories and play them alongside simultaneously, why choose when you can have them all?! allows to choose the pivot of the animation;delayed: get any other factory and copy it multiple times, increasingly skipping some frames on each one! very cool effects are made here!For more details please look at their docstrings, which are very complete.Bar FactoriesCustomizing bars is nowhere near that involved. Let's say they are "immediate", passive objects. They do not support animations, i.e. they will always generate the same rendition given the same parameters. Remember spinners are infinite generators, capable of generating long and complex sequences.Well, bars also have a meta factory, use closures to store the styling parameters, and receive additional operating parameters, but then the actual factory can't generate any content by itself. It still needs an extra parameter, a floating-point number between 0 and 1, which is the percentage to render itself.alive_barcalculates this percentage automatically based on the counter and total, but you can send it yourself when in themanualmode!Bars also do not have a Bar Compiler, but theydo provide the check tool!! 🎉You can even mix and match wide chars and normal chars just like in spinners! (and everything keeps perfectly aligned 😅)Use the check tools to your heart's content!! They have even more goodies awaiting you, even real-time animations!Create the wildest and coolest animations you can and send them to me!I'm thinking about creating some kind ofcontribpackage, with user-contributed spinners and bars!Wow, if you've read everything till here, you should now have a sound knowledge about usingalive-progress! 👏But brace yourself because there is even more, exciting stuff lies ahead!Maintaining an open source project is hard and time-consuming, and I've put much ❤️ and effort into this.If you've appreciated my work, you can back me up with a donation! Thank you 😊AdvancedThe Pause MechanismOh, you want to pause it altogether, I hear? This is an amazing novel concept, not found anywhere AFAIK.With this you get to act on some itemsmanually, at will, right in the middle of an ongoing processing!!YES, you can return to the prompt and fix, change, submit things, and the bar will just "remember" where it was...Suppose you need to reconcile payment transactions (been there, done that). You need to iterate over thousands of them, detect somehow the faulty ones, and fix them. This fix is not simple nor deterministic, you need to study each one to understand what to do. They could be missing a recipient, or have the wrong amount, or not be synced with the server, etc., it's hard to even imagine all possibilities.Typically, you would have to let the detection process run until completion, appending to a list each inconsistency it finds and waiting, potentially a long time, until you can finally start fixing them... You could of course mitigate that by processing in chunks, or printing them and acting via another shell, etc., but those have their own shortcomings... 😓Now, there's a better way! Simply pause the actual detection process for a while! Then you just have to wait till the next fault is found, and act in near real-time!To use the pause mechanism you just have to write a function, so the code canyieldthe items you want to interact with. You most probably already use one in your code, but in theipythonshell or another REPL you probably don't. So just wrap your debug code in a function, then enter within abar.pause()context!!defreconcile_transactions():qs=Transaction.objects.filter()# django example, or in sqlalchemy: session.query(Transaction).filter()withalive_bar(qs.count())asbar:fortransactioninqs:iffaulty(transaction):withbar.pause():yieldtransactionbar()That's it! It's that simple! \o/Now rungen = reconcile_transactions()to instantiate the generator, and whenever you want the next faulty transaction, just callnext(gen, None)! I love it...Thealive-progressbar will start and run as usual, but as soon as any inconsistency is found, the bar will pause itself, turning off the refresh thread and remembering its exact state, and yield the transaction to you directly on the prompt! It's almost magic! 😃In [11]: gen = reconcile_transactions()

In [12]: next(gen, None)
|█████████████████████                   | 105/200 [52%] in 5s (18.8/s, eta: 4s)
Out[12]: Transaction<#123>You can then inspect the transaction with the usual_shortcut ofipython(or just directly assign it witht = next(gen, None)), and you're all set to fix it!When you're done, just reactivate the bar with the samenextcall as before!! The bar reappears, turns everything back on, and continueslike it had never stopped!! Ok, it is magic 😜In [21]: next(gen, None)
|█████████████████████                   | ▁▃▅ 106/200 [52%] in 5s (18.8/s, eta: 4s)Rinse and repeat till the final receipt appears, and there'll be no faulty transactions anymore. 😄Loop-less useSo, you need to monitor a fixed operation, without any loops, right?It'll work for sure! Here is a naive example (we'll do better in a moment):withalive_bar(4)asbar:corpus=read_file(file)bar()# file was read, tokenizingtokens=tokenize(corpus)bar()# tokens generated, processingdata=process(tokens)bar()# process finished, sending responseresp=send(data)bar()# we're done! four bar calls with `total=4`It's naive because it assumes all steps take the same amount of time, but actually, each one may take a very different time to complete. Thinkread_fileandtokenizemay be extremely fast, which makes the percentage skyrocket to 50%, then stopping for a long time in theprocessstep... You get the point, it can ruin the user experience and create a very misleading ETA.To improve upon that you need to distribute the steps' percentages accordingly! Since you toldalive_barthere were four steps, when the first one was completed it understood 1/4 or 25% of the whole processing was complete... Thus, you need to measure how long your steps actually take and use themanual modeto increase the bar percentage by the right amount at each step!You can use my other open source projectabout-timeto easily measure these durations! Just try to simulate with some representative inputs, to get better results. Something like:fromabout_timeimportabout_timewithabout_time()ast_total:# this about_time will measure the whole time of the block.withabout_time()ast1# the other four will get the relative timings within the whole.corpus=read_file(file)# `about_time` supports several calling conventions, including one-liners.withabout_time()ast2# see its documentation for more details.tokens=tokenize(corpus)withabout_time()ast3data=process(tokens)withabout_time()ast4resp=send(data)print(f'percentage1 ={t1.duration/t_total.duration}')print(f'percentage2 ={t2.duration/t_total.duration}')print(f'percentage3 ={t3.duration/t_total.duration}')print(f'percentage4 ={t4.duration/t_total.duration}')There you go! Now you know the relative timings of all the steps, and can use them to improve your original code! Just get the cumulative timings and put them within a manual modealive_bar!For example, if the timings you found were 10%, 30%, 20%, and 40%, you'd use 0.1, 0.4, 0.6, and 1.0 (the last one should always be 1.0):withalive_bar(4,manual=True)asbar:corpus=read_big_file()bar(0.1)# 10%tokens=tokenize(corpus)bar(0.4)# 30% + 10% from previous stepsdata=process(tokens)bar(0.6)# 20% + 40% from previous stepsresp=send(data)bar(1.)# always 1. in the last stepThat's it! The user experience and ETA should be greatly improved now.FPS CalibrationSo, you want to calibrate the engine?Thealive-progressbars have cool visual feedback of the current throughput, so you can actuallyseehow fast your processing is, as the spinner runs faster or slower with it.For this to happen, I've put together and implemented a few fps curves to empirically find which one gave the best feel of speed:(interactive version [here](https://www.desmos.com/calculator/ema05elsux))The graph shows the logarithmic (red), parabolic (blue) and linear (green) curves, these are the ones I started with. It was not an easy task, I've made dozens of tests, and never found one that really inspired that feel of speed I was looking for. The best one seemed to be the logarithmic one, but it reacted poorly with small numbers.
I know I could make it work with a few twists for those small numbers, so I experimented a lot and adjusted the logarithmic curve (dotted orange) until I finally found the behavior I expected! It is the one that seemed to provide the best all-around perceived speed changes throughout the whole spectrum from a few to billions...
That is the curve I've settled with, and it's the one used in all modes and conditions. In the future and if someone would find it useful, that curve could be configurable.Well, the defaultalive-progresscalibration is1,000,000in bounded modes, i.e., it takes 1 million iterations per second for the bar to refresh itself at 60 frames per second. In the manual unbounded mode, it is1.0(100%). Both enable a vast operating range and generally work quite well.For example, take a look at the effect these very different calibrations have, running the very same code at the very same speed! Notice the feel the spinner passes to the user, is this processing going slow or going fast? And remember that isn't only the spinner refreshing but the whole line, complete with the bar rendition and all widgets, so everything gets smoother or sluggish:So, if your processing hardly gets to 20 items per second, and you thinkalive-progressis rendering sluggish, you could increase that sense of speed by calibrating it to let's say40, and it will be running waaaay faster... It is better to always leave some headroom and calibrate it to something between 50% and 100% more, and then tweak it from there to find the one you like the most! :)Forcing animations on PyCharm, Jupyter, etc.Do these astonishingalive-progressanimations refuse to display?PyCharm is awesome, I love it! But I'll never understand why they've disabled emulating a terminal by default... If you do use PyCharm's output console, please enable this on all your Run Configurations:I even recommend you go intoFile>New Projects Setup>Run Configuration Templates, selectPython, and also enable it there, so any new ones you create will already have this set.In addition to that, some terminals report themselves as "non-interactive", like when running out of a real terminal (PyCharm and Jupyter for example), in shell pipelines (cat file.txt | python program.py), or in background processes (not connected to a tty).Whenalive-progressfinds itself in a non-interactive terminal, it automatically disables all kinds of animations, printing only the final receipt. This is made in order to avoid both messing up the pipeline output and spamming your log file with thousands ofalive-progressrefreshes.So, when you know it's safe, you can force them to seealive-progressin all its glory! Here is theforce_ttyargument:withalive_bar(1000,force_tty=True)asbar:foriinrange(1000):time.sleep(.01)bar()The values accepted are:force_tty=True-> always enables animations, and auto-detects Jupyter Notebooks!force_tty=False-> always disables animations, keeping only the final receiptforce_tty=None(default) -> auto detect, according to the terminal's tty stateYou can also set it system-wide usingconfig_handler, so you don't need to pass it manually anymore.Do note that PyCharm's console and Jupyter notebooks are heavily instrumented and thus have much more overhead, so the outcome may not be as fluid as you would expect. On top of that, Jupyter notebooks do not support ANSI Escape Codes, so I had to develop some workarounds to emulate functions like "clear the line" and "clear from cursor"... To see the fluid and smoothalive_baranimations as I intended, always prefer a full-fledged terminal.Interesting factsThis whole project was implemented in functional style;It uses extensively (and very creatively) PythonClosuresandGenerators, e.g. allspinnersare made with coolGenerator Expressions! Besides it, there are other cool examples like theexhibitmodule, and the corespinner player/spinner runnergenerators; 😜Until 2.0,alive-progresshadn't had any dependency. Now it has two: one isabout-time(another very cool project of mine if I say so myself), to track the spinner compilation times and generate its human-friendly renditions. The other isgrapheme, to detect grapheme cluster breaks (I've opened anissuethere asking about the future and correctness of it, and the author guarantees he intends to update the project on every new Unicode version);Also, until 2.0alive-progresshadn't had a single Python class! Now it has a few tiny ones for very specific reasons (change callables, iterator adapter, and some descriptors for the widgets).Everything else is a function, which generates other functions internally with some state on the parent, i.e.Closures. I've used them to create spinner factories, bar factories, the global configuration, the system hooks, the spinner compiler (which is also a bigFunction Decorator), evenalive_baritself is a function! And in the latter mostly, I dynamically plug several other functions into the main one (Python functions have a__dict__just like classes do). 😝To doenable multiple simultaneous bars for nested or multiple activities (the most requested, but very complex)reset a running bar context, i.e. run in unknown mode while "quantifying" then switch to definite modedynamic bar width rendition, which notices terminal size changes and shrink or expand the bar as needed (currentlyalive_bardoes notice terminal size changes, but just truncates the line accordingly)improve test coverage, currently at7789% branch coverage (but it's very hard since it's multithreaded, full of stateful closures, and includes system print hooks)create acontribsystem somehow, to allow a simple way to share cool spinners and bars from userssupport colors in spinners and bars (it's very hard, since color codes alter string sizes, which makes it tricky to synchronize animations, besides correctly slicing, reversing, and iterating fragments of strings whilealso maintaining color codesis very, very complex)update here: this may be much simpler now with the newCell Architecture!any other ideas are welcome!Noteworthy features already done ✅resuming computations support withskippeditemshelp system on configuration errorsreadable widgets to extract informationexponential smoothing algorithm for the ratesupport for usingstderrand other files instead ofstdoutunits with automatic scalingdual-line modecustomize final receipt widgetscustomize widgets rendition likemonitor,elapsed,statsbar title can be dynamically set, changed or removedexponential smoothing algorithm for the ETAjupyter notebook support, which works the same as in the terminal, animations and everythingcreate an unknown mode for bars (without a known total and eta)implement a pausing mechanismchange spinner styleschange bar stylesinclude a global configuration systemcreate customizable generators for scrolling, bouncing, delayed, and compound spinnerscreate an exhibition for spinners and bars, to see them all in motioninclude theme support in configurationsoft wrapping supporthiding cursor supportPython logging supportexponential smoothing of ETA time seriescreate an exhibition for themesChangelog highlightsCompletehere.3.1.4: support spaces at the start and end of titles and units3.1.3: better error handling of invalidalive_itcalls, detect nested uses of alive_progress and throw a clearer error message3.1.2: fix some exotic ANSI Escape Codes not being printed (OSC)3.1.1: support for printing ANSI Escape Codes without extra newlines, typing annotations inalive_it3.1.0: new resuming computations support withskippeditems, newmax_colsconfig setting for jupyter, fix fetching the size of the terminal when using stderr, officially supports Python 3.113.0.1: fix for logging streams that extend StreamHandler but doesn't allow changing streams3.0.0: units support with automatic and configurable scaling and precision, automatic stats scaling for slow throughputs, support for usingsys.stderrand other files instead ofsys.stdout, smoothed out the rate estimation, more queries into the currently running widgets' data, help system in configuration errors2.4.1: fix a crash when dual-line and disabled are set2.4.0: support dual line text mode; finalize function parameter in alive_it; improve logging support, detecting customized ones2.3.1: introduce ctrl_c config param; print the final receipt even when interrupted2.3.0: customizablemonitor,elapsed, andstatscore widgets, newmonitor_end,elapsed_end, andstats_endcore widgets, better support for CTRL+C, which makesalive_barstop prematurely2.2.0: bar title can be dynamically set, changed or removed; customizable refresh rates; final receipt can be hidden;click.echo()support; faster performance; safer detection of terminal columns;bar.currentacts like a property; remove Python 3.62.1.0: Jupyter notebook support (experimental), Jupyter auto-detection, disable feature and configuration2.0.0: new system-wide Cell Architecture with grapheme clusters support; super cool spinner compiler and runner;.check()tools in both spinners and bars; bars and spinners engines revamp; new animation modes in alongside and sequential spinners; new builtin spinners, bars, and themes; dynamic showtime with themes, scroll protection and filter patterns; improved logging for files; several new configuration options for customizing appearance; new iterator adapteralive_it; usestime.perf_counter()high-resolution clock; requires Python 3.6+ (and officially supports Python 3.9 and 3.10)1.6.2: newbar.current()method; newlines get printed on vanilla Python REPL; the bar is truncated to 80 chars on Windows1.6.1: fix logging support for Python 3.6 and lower; support logging for file; support for wide Unicode chars, which use 2 columns but have length 11.6.0: soft wrapping support; hiding cursor support; Python logging support; exponential smoothing of ETA time series; proper bar title, always visible; enhanced times representation; newbar.text()method, to set situational messages at any time, without incrementing position (deprecates 'text' parameter inbar()); performance optimizations1.5.1: fix compatibility with Python 2.7 (should be the last one, version 2 is in the works, with Python 3 support only)1.5.0: standard_bar accepts abackgroundparameter instead ofblank, which accepts arbitrarily sized strings and remains fixed in the background, simulating a bar going "over it"1.4.4: restructure internal packages; 100% branch coverage of all animations systems, i.e., bars and spinners1.4.3: protect configuration system against other errors (length='a' for example); first automated tests, 100% branch coverage of configuration system1.4.2: sanitize text input, keeping \n from entering and replicating bar on the screen1.4.1: include license file in the source distribution1.4.0: print() enrichment can now be disabled (locally and globally), exhibits now have a real-time fps indicator, new exhibit functionsshow_spinnersandshow_bars, new utilityprint_chars,show_barsgain some advanced demonstrations (try it again!)1.3.3: further improve stream compatibility with isatty1.3.2: beautifully finalize bar in case of unexpected errors1.3.1: fix a subtle race condition that could leave artifacts if ended very fast, flush print buffer when position changes or bar terminates, keep the total argument from unexpected types1.3.0: new fps calibration system, support force_tty and manual options in global configuration, multiple increment support in bar handler1.2.0: filled blanks bar styles, clean underflow representation of filled blanks1.1.1: optional percentage in manual mode1.1.0: new manual mode1.0.1: pycharm console support with force_tty, improve compatibility with Python stdio streams1.0.0: first public release, already very complete and maturePython End of Life noticealive_progresswill always try to keep up with Python, so starting from version 2.0, I'll drop support for all Python versions which enter EoL. See their schedulehere.But don't worry if you can't migrate just yet:alive_progressversions are perennial, so just keep using the one that works for you and you're good.I just strongly recommend setting olderalive_progresspackages in a requirements.txt file with the following formats. These will always fetch the latest build releases previous to a given version, so, if I ever release bug fixes, you'll get them too.For new Python 2.7 and 3.5❯pipinstall-U"alive_progress<2"For new Python 3.6❯pipinstall-U"alive_progress<2.2"LicenseThis software is licensed under the MIT License. See the LICENSE file in the top distribution directory for the full license text.Maintaining an open source project is hard and time-consuming, and I've put much ❤️ and effort into this.If you've appreciated my work, you can back me up with a donation! Thank you 😊