Terminals seem like the very lowest common denominator for software platforms.
They have to work over SSH, they only show text, you can't do much with them.
Or can you?
Will McGoogan and team have been building Textual based on Rich,
which looks more like an animated web app than a terminal app.
And he has learned a bunch of lessons trying to maximize terminal-based apps.
He's here to share his seven lessons he's learned while building a modern TUI,
that is Text User Interface Framework.
This is Talk Python to Me, episode 380, recorded September 5th, 2022.
Welcome to Talk Python to Me, a weekly podcast on Python.
This is your host, Michael Kennedy.
Follow me on Twitter where I'm @mkennedy,
and keep up with the show and listen to past episodes at talkpython.fm.
And follow the show on Twitter via at Talk Python.
We've started streaming most of our episodes live on YouTube.
Subscribe to our YouTube channel over at talkpython.fm/youtube
to get notified about upcoming shows and be part of that episode.
This episode is sponsored by Microsoft for Startups Founders Hub.
Check them out at talkpython.fm/founders hub to get early support for your startup.
And it's brought to you by Sentry.
Don't let those errors go unnoticed.
Use Sentry.
Get started at talkpython.fm/sentry.
Transcripts for this and all of our episodes are brought to you by Assembly AI.
Do you need a great automatic speech-to-text API?
Get human-level accuracy in just a few lines of code.
Visit talkpython.fm/assemblyai.
Will, welcome back to Talk Python to Me.
Thank you.
It's good to be here.
Yeah, it's fantastic to have you here.
Really looking forward to talking about the progress you've made on Rich and Textual and your company,
which is pretty fantastic.
The show is not specifically about that.
It's more about all these fantastic lessons that you've learned while building it.
But, you know, of course, we'll get a chance to talk about it and give some updates too, I'm sure.
Cool.
Sounds good to me.
Yeah.
Well, normally I ask folks how they got into programming and people, if they want to hear that story,
they can go back and check out your episode 336 on Talk Python way back, way back last year, about a year ago.
It's a lifetime ago.
So much has happened.
In terms of what's happened with your projects and stuff, it really is kind of a lifetime ago.
So what I want to ask you instead is, you know, what have you been up to the last year?
Give us an update.
Yeah.
Okay.
So I founded Textualize.io, a new startup.
I've hired developers and we're very busy working on Textual and it's come on really well.
It's amazing.
There's more than I thought it would do, to be honest with you.
Oh, Textualize.io.
Yeah.
Textualize.
There we go.
The other one's for sale if you want it.
I get that all the time.
Sorry.
Yeah.
So you've been working on Textualize and you actually got some investments and you're hiring
and amazing.
I'm so happy for you.
Thank you.
Yeah.
When I first heard that, I'm like, well, what is the business model?
What is the business model here?
Like, what are you all working towards?
Like really trying to just leverage the terminal, right?
Even more.
Yeah, that's right.
So that's a very reasonable question that everyone asks.
So the first part is Textual, which will be open source and distributed just like any other
open source project.
But we'll add on this commercial service where you can take those Textual apps and then you
can put them in the web.
And then when they're on the web, we can charge companies and organizations a monthly fee for
various services such as accounts and things and maybe, you know, payment portals and things.
But there will be a very generous free tier for hobbyists and for open source projects that
want to do the same.
Fantastic.
So we have SAS for software as a service.
We have PaaS, P-A-A-S for platform as a service.
And the whole style of apps from Textual and to a lesser degree rich often go under the
terminology of TUI, a text user interface, right?
So TUI as a service, TAS, T-A-S.
T-A-S.
Yeah.
Are you creating a new as a service?
Yeah, I kind of like it.
TAS, you get, you know, in tech, we love our acronyms.
So if I don't invent at least one, I'll be disappointed.
That's right.
And if they can have like, you know, multiple cases, right, like a capital T-A-S, then I
think it's going to be great.
Yeah.
It's an even, it's one of the better types of acronyms.
Yeah.
Fantastic.
Now, I think this is great.
And, you know, there's one of the things I'm really fascinated about and for a long time
I've been trying to pay attention to and highlight is how do people go from open source projects
to business?
And when I first started the podcast and really started to think about these things, it seemed
like there was not that many great answers.
It felt like a lot of, well, here's my PayPal donation link or something.
And it just seemed like, well, okay, that might feel good as a thanks, but you cannot make
that your job to say, buy me a coffee most of the time.
Yeah.
But there is, there's a lot of progress lately, isn't there?
Yeah.
Some GitHub sponsors and similar programs.
You can get sponsorship and it can be enough to live on.
It's not easy.
The type of projects to get sponsored are the ones which are super critical to businesses.
And in that case, companies don't mind donating $300 a month.
And if you get enough of those, you could and three live on it.
And some people certainly do.
But I don't think it's practical for most people.
Even if your open source project is widely used and popular, it might not bring in enough
sponsorship to live on, unfortunately.
Yeah, unfortunately.
And so I do think GitHub sponsors really is kind of that done right.
You know, it's recurring.
It's automatic.
Yeah.
It has a social signal benefit, right?
Like you can see who is sponsoring which projects.
And so if you're an organization, right, like you could say, oh, look, our company sponsors
that project or whatever.
Yeah.
It's very well done.
Actually, I've got no problems with it.
It's done.
You can, like I said, you can contribute every month or you can contribute a one-off and you
can get your name mentioned on the project.
And the author of the project can offer various perks.
So I think it's nicely done.
The other one that I see really, this might be the most popular one, is take an open source
thing and then take away, help alleviate or completely solve the operational side of things.
Right.
You know, for example, we have MongoDB and then they have Atlas, which lets you push a
button and manage your cluster automatically inside your own AWS or Azure account.
We've got Streamlit, which just got acquired, has a lot of similarities to what you're doing.
You know, it's like it's got an open source version.
You can create these dashboards.
It's really cool.
But then how do you put them on the internet?
How do you maintain them?
Right.
And then, well, guess what?
There's a paid tier that just runs it in our cloud for you.
Right.
So it seems like a really great, really great path to proceed down for what you're doing here.
Yeah, that's right.
They've got a term for it.
It's called OpenCore.
So you use the open source part to drive adoption.
And then you can use that to sell some related services, which will just make life easier for
businesses and things because there's a long history of businesses making use of open source
projects and making a lot of money out of it.
But they haven't really put that money in the business.
So if the people that are building and maintaining these open source projects can also build side
businesses around the open source project, then everyone benefits because your code gets
maintained for the indefinite future and people can make a living from it.
Yeah.
If you would otherwise have to hire somebody to manage a Kubernetes cluster or whatever,
and instead you can pay $20 a month, that's a real good deal.
Exactly.
And you don't want to have to solve all these problems individually for everyone that uses it.
It's actually far better for someone to solve it once and for all, or at least reduce the
footprint for maintenance.
Yeah.
Who else better to figure out how to put it in the cloud than people who are creating it,
right?
Yeah, exactly.
Yeah.
Yeah, for sure.
Okay.
So fantastic.
I'm looking forward to TUI as a service to come on strong here.
That's great.
So maybe before we jump into the lessons learned, just tell people about what textual is and
then textual is built upon rich.
So give us a quick rundown on take whichever order you think is better to go first, rich
or textual.
I think rich.
I'll go in chronological order.
So I started rich.
Gosh, it must be like three years now.
And the idea was to be able to write colored text, the terminal in a nice, elegant way, and
also be able to build on that with larger components.
So we've got things like tables.
We've got progress bars.
We've got log messages, panels, all sorts of things.
And they're all using the same core rendering technology, which basically takes your objects
and then turns it into ANSI codes and text.
And that got fairly popular, shall we say?
I was surprised.
I would say.
I would say so too.
Yeah.
I mean, it's in pip now, which still blows me away because that means that virtually every
Python developer is running my code, which also scares me just a little bit.
This is nuts, right?
Because I mean, if you have Python, you have pip.
So you have rich.
Is it a package with pip as a dependency or is it vendored in?
It's vendored because you've got a chicken and egg problem with pip.
Right.
Because pip is how you get the things, right?
Exactly.
So everything is vendored.
Pip is just one project with lots of vendored projects inside of it.
Rich, for people who haven't seen it in action, it's not just like Colorama or something,
which I'm a big fan of Colorama.
But that's just about how do I make this line this color or whatever.
But you're talking about like tables with auto, you know, auto ellipsing and all sorts of
really, really powerful content, right?
Yeah.
And some things which you might take for granted in the browser, like text wrapping, that wasn't
easy to do prior to Rich.
It's more complex than you might think.
Things like emoji and Chinese characters, those take up two cells in the terminal.
If you use the built-in text wrap module, that won't account for that.
And your text wrapping will be misaligned.
So I wanted to solve those kind of problems.
I just want it to be effortless.
You know, I just, you just say, print this text and it'll wrap it for you, which generally
makes it a lot more readable.
Yeah, absolutely.
There's a lot, there's a lot going on there.
I would say so.
And I don't know how many projects are using Rich now, but it's, there constantly seems
to be like, and now this has Rich support.
So it has much better, you know, output or it's more understandable or whatever, right?
A lot of cool things happening because of it.
I'm delighted every time I read one of those.
I do try to make it quite easy to drop things in.
For instance, if you're printing out Python data structures, you can use a Rich method and
you'll get pretty printing and colorizing built in.
And things like exception handling, we've got very pretty exceptions that show you snippets
of code and can, you know, highlight the line where the error occurred.
And you can add those with just a few lines.
So I think that's, that's kind of pushing adoption is the fact that there is very low barrier to
entry.
Yeah.
And it's just beautiful, right?
It's easy to make beautiful UIs.
And if you make it easy for people to make nice looking things, they, they want to use it.
I mean, it's not as used as much these days, but think of when Bootstrap came
on the scene, you know, eight years ago or 10 years, whatever it was 10 years ago, every,
everything started to look like Bootstrap because you could just apply this magic and like,
oh, everything looks like professional, but we're not professional.
And I feel like Rich is a little bit like that for two weeks.
I think so.
Cause it was, it was very difficult to add pretty formatting prior to Rich.
I mean, there were libraries that existed, but they didn't integrate very well.
Rich kind of combines all that functionality together.
Um, so it's, it's very easy to, to add a pretty content and, it's not just pretty for pretty
sake, you know, pretty can also mean more readable.
Um, you know, developers, we get presented with pages and pages of content that we learned
to decipher and pick out meaning.
Um, but we can do that much quicker if there's been some forethought into, into how it's presented
and rendered in the terminal.
And Rich does give you that capability.
This portion of talk Python to me is brought to you by Microsoft for startups founders hub.
Starting a business is hard by some estimates.
Over 90% of startups will go out of business in just their first year.
With that in mind, Microsoft for startups set out to understand what startups need to be successful
and to create a digital platform to help them overcome those challenges.
Microsoft for startups founders hub was born.
Founders hub provides all founders at any stage with free resources to solve their startup
challenges.
The platform provides technology benefits, access to expert guidance and skilled resources, mentorship
and networking connections, and much more.
Unlike others in the industry, Microsoft for startups founders hub doesn't require startups
to be investor backed or third party validated to participate.
Founders hub is truly open to all.
So what do you get if you join them, you speed up your development with free access to GitHub
and Microsoft cloud computing resources and the ability to unlock more credits over time.
Help your startup innovate.
Founders hub is partnering with innovative companies like open AI, a global leader in AI research and
development to provide exclusive benefits and discounts through Microsoft for startups founders
hub.
Becoming a founder is no longer about who you know, you'll have access to their mentorship network,
giving you a pool of hundreds of mentors across a range of disciplines and areas like idea
validation, fundraising management and coaching sales and marketing, as well as specific technical
stress points.
You'll be able to book a one-on-one meeting with the mentors, many of whom are former founders
themselves.
Make your idea a reality today with a critical support you'll get from founders hub to join the
program.
Just visit talkpython.fm/founders hub, all one word, no links in your show notes.
Thank you to Microsoft for supporting the show.
Jamie on the audience says, "I love Rich.
Rich traceback is so helpful."
Maybe just quickly touch on the traceback.
And then also, Demetrius is asking about, is this for Jupyter?
Is it only a thing for Linux terminals?
And maybe sort of touch on those two things and we'll talk textual.
Tracebacks, yeah, you can install a traceback handler and it will capture unhandled tracebacks
and present them nicely in the terminal.
What was the other question?
Sorry.
Where does this apply?
Like, can I use this on Windows?
Can I use it on Mac?
Is it for Jupyter?
What is this?
Pretty much everywhere.
So you can use it on all the major platforms, Linux, Mac and Windows.
And yes, it does work in Jupyter.
So it will convert the ANSI codes to HTML automatically.
That's actually the most impressive thing to me.
That must be a lot of work to maintain all these different output destinations and whatnot.
The terminal is not too bad.
So macOS and Linux are frankly the same as far as I'm concerned.
Windows has a number of tweaks and hacks because there's two windows basically.
Newer Windows has virtual terminal sequences which make it just like the Linux and Mac
terminal and that's terrific.
But we also support the older style terminal which is quite limited.
And the review like...
Right, right.
CMD.exe is not nearly as powerful as the new Windows terminal.
No, it's not.
So we have to make some compromises and some sacrifices.
It works.
It's usable.
It won't look quite as pretty.
But there's a lot of people using that out there.
So it's important to maintain.
Yeah, you can't really drop it.
Yeah.
No, you can't drop it.
Okay.
And then the other thing that's more directly related to your article and to your business,
although obviously one's a building block.
So same as Textual.
Yeah.
So Rich was for writing kind of static content into your kind of terminal, your scroll back
buffer.
What Textual does is it uses Rich, but it completely takes over your terminal to create an application.
You don't see a command prompt, but Textual will handle key presses and mouse movements and
it'll render quite sophisticated applications, which look more like web apps than the previous
generation of Tuis.
Yeah.
But under the hood, it's using the same code that renders tables in Rich.
It's kind of a, I call it a rendering engine.
It's designed to take sort of abstract data and then turn it into what I call segments, which
are basically a piece of text with a style.
It's pretty amazing.
You know, if you look at the UI, it's got, it really does feel a bit like a web app.
It has say like a title bar across the top and you know, this isn't just like text you
printed out on the top of the output.
No.
It's stuck to the top, like a nav bar, I guess is the right word.
You have these different widgets you can put in line, like code highlighting.
You've got a text widgets with like scrolling within scrolling.
You've got animated sidebars that pop out all these cool different aspects, right?
Even a hotkey support for like D to toggle dark and light mode if you want.
Yeah.
We've even got data tables now, which are pretty cool.
They look kind of like little Excel windows type of spreadsheets.
Oh, fantastic.
Yeah.
So it's, I think my background is as a web developer, basically.
So I want to make something which anyone who knows how to use a web, which is pretty much
everyone, they would be comfortable using one of these applications.
You could just sit them down in front of it and they'll know exactly what to do.
They'll know that this is a menu.
They can click that.
Here's a scroll bar.
I don't know how to use a scroll bar.
It'll work with a mouse wheel or two fingered scroll on the trackpad.
It'll just be very, very familiar.
But at the same time, I also want to keep it keyboard focused because one of the benefits
of TUI is it doesn't interrupt your flow.
You know, you can be at the terminal typing commands, enter into a TUI and you could work
with that and use the keyboard and they can drop back straight into the command line.
So it's kind of this marriage of the command line and more sophisticated applications which work a lot like desktop and web.
previously when people wanted to create apps like this, well, one, it was very difficult to do.
But if they wanted to come somewhat close to these types of things, they would use a library called Curses or something like that, right?
Yeah.
And Curses has been around for a long time, I think decades.
I can't remember off the top of my head, but I think in the 90s it came around.
And, you know, people have done some very cool things with it and have been like attempts to improve upon it.
But I think still it does take, you have to be very well motivated to create an application with Curses because you're going to have to deal with some quite archaic issues.
Yeah.
I haven't done a ton with it, but it feels to me like the equivalent of saying like textual versus Curses would be like you could either use something like Pygame where you can give some sprites and they can move around the screen.
Or you could fire up OpenGL or DirectX or Metal and you could start rendering pixels on your own.
Like you could accomplish the same UI, but one is tremendously difficult.
The other one kind of gives you much higher level building blocks to accomplish the same thing.
Yeah, exactly.
So textual is more abstract.
You don't have to plot individual characters.
You just say put a text box in this part of the screen and a button in this part of the screen and, you know, textual handles the rest.
So it's less archaic aspects of terminals that go back decades that you have to think about.
Let the framework handle it and handle the differences, right?
Yeah, exactly.
That's what computing should be.
It should make your life easier.
You shouldn't have to think about, you know, decades.
You know, you hear in science people talking about, you know, standing on the shoulders of giants, of like building on Einstein and whatever.
There's nothing else that stands on the shoulders of previous work like software, right?
It's just like layers and layers and layers.
We don't have to think about that anymore.
We now have a different set of concepts to think in.
Now let's go build, you know?
Yeah, yeah, which is fine for the most part, as long as you don't ever have to look behind the curtain.
You know, it's the same with any piece of computing.
It presents a nice, clean interface, but there's a lot of effort to get there.
It's like an iceberg.
You only see the very tip of it, but two-thirds are, like, below the water level.
Basically, if it becomes a leaky abstraction, right?
And then you've got to deal with the underlying layer, but not exactly anyway.
Exactly.
All right, last question, and then we'll move off.
Talk about the lessons.
Luca asks, will Textual have a polyfill for blink, like text blinking in terminals, which
don't support it, or, you know?
But basically, I guess more interesting, does it have polyfills and, like, other ways to
make stuff happen that's not naturally supported?
You know, it could quite easily.
You could set up a timer called set interval, which would toggle something to show the blink
or hide the blink.
I don't think I want to support in Textual because I don't want Textual apps to be blinking.
It's a terrible user interface for the most part.
The exception would be a cursor.
Sometimes you want a cursor to flash so you can see where it is.
I think I might leave that one up to developer to implement, and it probably is, like, a two-line
job.
I can see a future where some movie has, like, a fake hacker UI implemented in Textual, and they
probably have a blink thing about, like, when something's going to blow up going on.
All right, let's move on.
Let's go through your lessons.
So this is what you've been building and what you are building.
And obviously you've worked on Rich for a long time.
You've worked, you and your colleagues at the company, for a year now.
So tell us about the lessons.
Let's go through these.
Yeah.
Okay.
So terminals are fast.
This might surprise people.
And I can understand why.
It surprised me.
Yeah.
Because when you use a terminal, you type a few characters, and the characters appear
on screen.
You hit return.
You know, half a second later, you'll get a response with some text.
And you don't think of terminals as being fast.
But nowadays, they're built on the same technology that runs video games.
So it uses the GPU to update the terminal with new characters, which have a background and
a foreground.
And it turns out that if you can write updates at 60 frames a second from the Python code,
the terminal will happily display it.
I was surprised at how smooth it was.
We had 60 frames per second animation of something moving across the screen, and it looked silky smooth.
And that was updating the entire screen, like every frame.
Wow.
I'm really impressed with the fact that so many of these terminals are hardware accelerated,
like GPU accelerated.
And I'm just poking around.
What am I running here right now?
I'm running iTerm.
And it has all these settings for GPU render redraws, and basically the maximum frame rate.
And so things like this.
It's just, it's, you would never expect to go find hardware acceleration features inside
the terminals.
But they have them.
They do, yeah.
And it's quite strange, because not much software takes advantage of that.
I mean, your day-to-day work at the terminal, it doesn't need to be fast.
It really doesn't.
As long as it can add new data, you know, within a second, you're probably quite happy.
So these terminals have been getting faster and faster and faster.
Some of them, like I use Kitty and something called Westerm.
And those are incredibly fast.
They're really optimized at getting things on the screen.
As in this thing?
Most software.
Yes.
Kitty's excellent.
It's super fast.
But most software doesn't need it.
You know, it's like you use Vim or HTOP.
HTOP updates once a second.
So it's like the developers of these terminals are making it faster and faster and faster.
But there's very little software that makes use of it.
Except for like sort of hobbyist demos where they do like video to text things.
So you can see like your face that's made up with ASCII characters, which is fun, right?
But you don't think that's not really productivity software.
So I was very pleased that when I started working on Textual that it wasn't a bottleneck.
You know, if I could write things fast enough to terminal, the terminal would happily accept it and render it.
That opens the possibility for so many things if you can get high frame rates.
You know, and the iTerm default was limit frames to 60 frames a second.
60 frames a second, that's plenty fine for really smooth animations.
Yeah.
And, you know, I think it's probably what my LCD monitor is stuck at, right?
So that's as fast as you're going to see it basically anyway.
That's it.
And it can't, there's not much use in it being any faster because in order to see greater than 60 frames a second,
you have to have something that's moving greater than 60 frames a second.
And generally, you don't want things flying about your terminal, unless maybe you're making a video game.
We use animation quite sparingly for nice kind of things like when a panel slides in, sidebar pops in.
It's quite smooth.
We can also use it for fades.
So we can set the opacity of a block of text and have it fade away and fade in again.
And that's sometimes quite useful if you want to draw the user's attention to the fact that you've added a new item.
Rather than having it appear immediately, it'll fade in.
So we can use animation in those places.
But again, it doesn't need to be anywhere near 60 frames a second.
No, it doesn't have to.
But I do agree that little bits of animation are super important for highlighting things that people need to pay attention to without much effort.
And it doesn't have to be so bright colored or right in the way, right?
If you just have a little thing slide down that says, you know, this job has finished while the other work is happening.
That's really nice.
And you have a video here on this article, the seven things article that we'll link to, obviously.
And it shows you running this thing called BasicPi.
Is this included with Textual or is it?
Yeah, that's one of our sandbox apps.
So whenever we're testing something, we just put it in Basic.py.
So that shows off like a number of the features we've got.
I think that video is quite old.
We've got some more cool stuff.
I'm sure you do.
Yeah.
But one of the things that's really nice is it shows a lot of the animations.
And one of the animations is changing the theme.
Another one is to pop out the side navigation.
And those are really nice, right?
The fact that that happens at a frame rate fast enough that it looks completely smooth means it doesn't just look like some terminal app where a thing clicks in and then it clicks out.
It actually feels really, you know, really nice and polished.
Yeah, we've kind of identified there's like a sliding scale of animation.
At one end, you've got like the scroll bars.
It might not be obvious, but those animate.
So if you click below it, it will scroll smoothly downwards.
And also it will filter when you click and drag.
It will filter sort of frames between the motion of the mouse.
And it makes the scrolling look a lot more smooth.
That's not obvious, but that is a use of animation.
And at that end of the scale, I think most people would agree that's good.
That's a bonus.
At the other end of the scale, we've got things which are a bit gratuitous, like the sidebar.
I like it, but some people might think, I don't want to be distracted by this animation.
I just want it to appear instantly.
I want it to be more, feel more snappy rather than animated.
So I think what we're going to do is we're going to have like a switch where you can decide as a user what kind of animation you like.
If you just want the scroll bars, you can set it to be quite low.
And that would make the sidebar pop in instantly and disappear instantly.
But if you love the animation, you can just whack it up to the maximum.
And then it will use animation wherever it can for fading and sliding, etc.
For example, with a scroll bar, that's not actually the terminal scrolling it.
That's textual redrawing the screen and reprocessing it.
If it's not smooth, if it just goes clunk, clunk, clunk, it's super hard to track when I scroll down.
Where do I continue from?
And that kind of stuff, right?
It's not just there to make it feel good or feel smooth.
It really has an important effect.
Yeah, if you press the page down, if it was to jump down instantly, you wouldn't be able to find your place in the text again.
Your eye would just have to focus in to find where you're reading.
But if it scrolls in over, say, 300 milliseconds, your eye tends to follow where you were reading.
So you move your eye, you follow the animation, and then you're sort of reading again from the top.
And that's actually beneficial.
It's not just eye candy.
It's not just gratuitous use of a feature, you know?
Yeah, absolutely.
It's very, very helpful.
So you talked about all of this happening without flickering in the terminal.
We already discussed the GPU accelerated, hardware accelerated aspects.
But you said also that's not necessarily enough.
So there's a couple of tricks that you highlight here.
The protocol of terminals came about over many decades.
And it wasn't designed to avoid flickering.
I think when people built the protocol...
They never imagined what you would be trying to do with it.
No, they didn't think someone was going to be animating it at 60 frames a second.
So there's no...
Well, there's very little help to reduce flicker.
So there's a few things that you can do.
One I've discovered is it's better to overwrite content without clearing it.
So if you want to animate, say, a piece of text, make it move, you might think, I'll clear this text, and I'll draw on top of it.
But what that does is it'll introduce a potential for a frame where you see where it's cleared.
And then you see the frame where it's updated, and that'll cause flicker.
So if you just overwrite the content without clearing it, then that reduces the chance of flickering.
Yeah, I've also seen sometimes some of these progress bars or the little tables updating.
They seem to write new whole sections.
And it's like filling your history with every frame.
Your terminal history is full of every frame and stuff like that.
If you start to resize it, and there's all this weird stuff that happens if you're not actually overwriting it.
Textual goes into what's called application mode.
This is a feature of terminals.
So when you're in application mode, you don't have a scroll back buffer.
The scroll back buffer has that problem.
It'll tend to fill up with garbage if you're trying to animate things.
And it's also quicker because the terminal doesn't have to worry about appending data and moving it, etc.
So Textual goes into application mode.
And it's generally much faster and able to reduce that flickering.
Trick two?
Trick two.
One of the problems with the protocol is that it's not aware of flickering.
It doesn't know when to paint the screen.
So you're sending it data.
It needs to know when to update the screen.
If you write lots of small pieces of data, say you're going to update a character over here, you're going to update a progress bar here.
It might update a few of them and then paint the screen and then update some others and paint the screen.
And it's that brief moment where you've got half an update, which is what causes flicker.
So what I've discovered is if you batch all your updates into one write, so you just write standard output the ones rather than several writes, that will reduce the potential flicker quite a bit.
Interesting.
Of course it would.
And I've never really thought about it.
When you're doing regular pixel-based graphics programming, OpenGL, DirectX, those types of things, they often set up what's called a double-buffered mode, where you actually draw the screen on a hidden piece and then you swap that to be in one Vsync to be the screen.
So you don't see the pieces streaming in as it goes.
And this is the same equivalent thing for terminals, huh?
It's like double-buffered mode.
You write to it like a whole thing and then you make it the screen.
It's a very similar concept.
The terminal doesn't give you that.
It doesn't have an invisible buffer that you can take your time to draw to.
So you have to implement that yourself.
But yeah, essentially, we're implementing a double-buffer.
And the third one has to do with synchronized output.
Doing the one write thing, that's very helpful.
But there's also a fairly recent addition to the protocol, which helps with this.
Basically, you write an escape code which says, I'm beginning an update.
And then you write your data.
And then you write another escape code which says, I've finished the update.
But between those two escape codes, the terminal won't update.
It'll only update at the very last moment.
So that's kind of accomplishing the same thing as the double-buffer.
Yeah, similar.
So we do both because not all terminals support these new escape codes.
So by doing both, we can ensure that it's flicker-free on newer terminals and older terminals.
Yeah, fantastic.
Speaking of different terminals, Kim in the audience asks, is application mode that you talked about earlier with the buffer a universal across all the terminals?
It is, yeah.
It's been around for a long time.
If you've used HTOP or anything like it or a full-screen terminal app, that'll use application mode.
This portion of Talk Byte Under Me is brought to you by Sentry.
You know Sentry as a longtime sponsor of this podcast.
They offer great error monitoring software that I've told you about many times.
It's even software that we use on our own web apps.
But this time, I want to tell you about a fun conference they have coming up.
Deploying new code can be a lot like making a really great sandwich, taking a bite, and then having all the contents fall out.
Exciting, chaotic, and maddening.
If you know the feeling, then Dex by Sentry might be for you.
This is a free conference by developers for developers, where you sort through the madness and look for ways to improve your workflow productivity.
Join Sentry for this event in San Francisco or virtually on September 28th and discover new ways to make your life a little easier.
Save your seat now at talkpython.fm/Dex.
That's talkpython.fm/D-E-X.
The link is in your show notes.
Thank you to Sentry for supporting Talk Python to me.
Final question while we're on this performance thing.
When will we see Doom implemented?
People keep asking that.
I don't know.
It might happen.
I think it has to happen.
I mean, Doom runs virtually everywhere.
It does.
Maybe the first Doom.
Maybe not Doom Eternal.
Yeah, yeah.
We're thinking the original Doom that really was amazing.
Right after Castle Wolfenstein.
Very good stuff.
Yeah, I love that.
All right.
So that was all some really interesting stuff about terminals.
But we also have some other recommendations and discoveries that are pretty awesome, even if you're doing other types of programming.
And one of them is Dict Views.
Apparently, I used Dict Views a lot, but I didn't really know they had a special name.
Tell us what are Dict Views and then why are they useful?
So Dict Views are the object returned from a dictionary when you call items or keys.
And I think almost all Python developers have used this, but we tend to use them to iterate over to get the keys and the items.
What you might not know is these are special objects.
They're not just simple iterables.
And they act a lot like sets, which kind of makes sense.
Because if you've got a dictionary, the keys are all unique.
So you can't have a repeating key.
So if you've got a key view, it's set-like, which means you can do set-like operations on it.
Right.
You know, ask the quick question, like, is this thing I'm about to add in here or not?
Something like that, right?
Yeah.
And you can take two different, you can take a set and a key view and combine them and see the intersection and see what keys are common in both or neither.
And those type of operations turned out to be very useful for what I was using it for.
And to be honest, I think this might be the first time I've ever used that in anger.
It's one of the things that you read about in the notes for the latest Python.
And you think, I can't imagine I'll ever use that.
And I didn't for years, but then I had a problem where in Textual, when we do a new update via CSS, we've got two different data structures.
One with the old positions of things and then one with the new positions of things.
What we want to do is compare those two data structures and find the things which have moved or have changed size.
And I started writing code for this and it wasn't straightforward code.
It was a couple of hours in.
I had written a lot of code and I think, that's a bit too slow.
It's a bit too awkward.
And only then did I remember the fact that you could use these key views as set operations.
And the code turned out to be almost a one-liner.
And, you know, I was so happy about this.
I forgot the fact I'd spent two hours writing code that I had just deleted.
Let me give people an example here who are listening.
So if you check out the blog post, there's a nice code example.
And, you know, the UI of Textual is made up of all these widgets.
They've got names like header, footer and sidebar.
And they have boundaries and regions, rectangles.
And as the UI changes, you might only want to redraw the delta, not the whole thing for performance reason, right?
And so given the old view and the new view, the question is, well, what widgets have changed?
And so you just say render map dot items, carrot or symmetric difference with the other newer frame dot items.
And that tells you these are the pieces that have to be redrawn, right?
Yeah, that gets exactly the information needed.
That tells me which things are new, which things have been removed, and then which things have changed position.
And those three types of things are the things that I need to redraw on the screen.
And, you know, it's a one-liner.
And I get that information for free because it is very fast.
It runs at the C level and produces just information that I needed.
Honestly, this surprises me that this is possible.
I would totally expect that with dot keys because keys have to be set like.
But items, you could have the same item assigned to different keys all over the place and stuff.
So it's a little interesting here.
The item is, you know, obviously the key plus the value.
So together, they're unique.
The values are not unique.
Oh, I see.
That's right.
Because that's a tuple of the key.
I see.
Got it.
Okay.
So the keys are guaranteed to be unique.
That makes sense.
That's therefore, yeah, whatever you add to it.
It's not going to make it less unique by adding stuff.
More possible differences.
All right.
So not totally related to this, but I got to ask it because it's just so meta.
Andrew out in the audience asked, can you embed a terminal within a textual app?
I can't.
I've been asked this a few times.
I'm just, I'm wondering what people want to use this for.
But in theory, it should be possible.
It means I'd have to write a software layer which interpreted all the escape codes and then
translated that to like a region of the textual app.
But in theory, yes, it's possible.
Will we do it?
Maybe.
One day.
It's not the highest on the priority, but maybe.
Yeah, maybe.
Okay.
Very interesting.
Is there something like a textual.contrib?
You know, these extras that people put in, like there's Django.contrib and there's other
contribs.
Yeah.
Not currently, but I'm really hoping that the community takes it and starts building things.
So you could search PyPy for textual underscore and get lots of widget libraries and various
add-ons.
Is there like a plugin or extension aspect?
Anything official like that?
Yes.
So the widgets are designed to be built that way.
Widgets are like independent portions of the screen that can handle events and things and
updates.
And those are bundled up into separate libraries, third-party libraries.
So that would be the easiest way to implement something.
You could implement anything you wish, like a full IDE if you wanted to and just import
it as a Python library.
Sure.
Related to using dictvues for speed, it's really hard to beat caching for speed, isn't
it?
Yeah.
Caching is awesome and it's one of the things which allows textual to be fast.
So LRU cache, I think a lot of people have used it, but maybe not appreciated how fast
it is.
Caching and generating the key is done at the C level.
So it's super fast and you can use it for quite small functions.
We've got a lot of calculations which are pretty quick.
You know, they're well under milliseconds, like fractions of a millisecond.
But we do them a lot of times.
So we might do them like 10,000 times.
But if we introduce the LRU cache, the time it takes to do those function calls becomes time
it takes to essentially do a dictionary lookup.
Then they become very fast.
So that can, that led to quite surprising speed ups.
Yeah.
So you have an example here that you talk about where you're given a couple of regions
regions and you do the intersection of those two, right?
A region being like a rectangle like thing.
Yeah.
So you've got to figure out, well, which is the top left most and like all those, there's
a bunch of comparisons to find the overlapping rectangle if it exists.
Right.
And then, yeah.
It's not very complicated.
It's not very slow.
It's just doing sort of arithmetic and it's working on local variables.
That's generally the fastest kind of code you can expect from Python.
You also, you create a region object which has to reserve some memory.
But if you put the LRU cache on it, that becomes a dick lookup.
So it becomes very fast.
And the type of calculations we're doing, we often do the same ones many, many times.
So we'll use the same two values to find intersection.
Like that the first time you update the screen, you calculate some intersections.
And the second time, maybe one or two items has moved.
But most of the same calculations are done again.
So LRU cache ensures that you don't do those calculations again.
You just pick them out of a dictionary.
And yeah, big wins.
And it's so easy to just copy that one line and everything works.
Yeah.
Also, it's kind of related to what we're going to get to here.
But this kind of code is fantastic because in terms of caching, because it doesn't really
depend on something that could change behind the scenes.
It doesn't have hold of like weird pointers that other, you know, could change in other ways.
And so it's very, very deterministic.
It's going to give you the same answer every time.
And, you know, it's not like the cache is going to get weirdly stale, right?
Yeah, exactly.
So it's an immutable object.
I think region is actually a named tuple.
And you get all those benefits.
There's no side effects.
You can write these functions that have got an input and an output.
And it doesn't depend on any other state.
And when you have that kind of function, I think they call it a pure function.
It's caching works beautifully.
There's no hidden surprises.
And also, I think, makes your code more easy to reason about.
You can trace it, you know, just manually.
You can tell what's going on.
Just by looking at one function, you can see the full story.
Sure.
Yeah.
Immutable objects.
If your code can use immutable objects, I think you should favor it.
It doesn't always make sense.
But immutable is definitely best.
Yeah.
Definitely easy to reason about.
Kim in the audience says, presumably, the memory cost of caching many frequently called functions isn't a big issue on a reasonable machine.
And maybe it's worth pointing out the max size parameter you passed to the LRU cache.
That's right.
So you set a maximum size.
And if you add more items than that, it'll throw out the oldest one.
So you can define the set, the maximum size that you will record.
And it depends on how you use it.
If you tend to have like a common set of calculations, which you're doing repeatedly, and those will kind of be in the cache most of the time.
And then you might have some calculations which happen infrequently, you know, combinations of input and output.
And then those get recalculated occasionally.
But it's still a big win.
And for 4,096 items for that, the regions name tuple is quite small.
So that keeps the memory usage to something reasonable.
I did want to highlight this project called Async Cache.
Because I think, I haven't checked lately, but I think the LRU cache is only for synchronous functions.
I think so, yeah.
Yeah, I think so too.
You know, maybe it's been upgraded.
I haven't checked.
But if it's still true, there's this project called Async Cache, which has nearly the same UI, but applies to Async functions.
So basically, the decorator has to return a function, which is first checks the cache and then calls the function.
For the Async version, that has to be an Async function returned out of the decorator.
Hence the difference, right?
And so this Async Cache has something really similar where you can have an LRU cache where you set the max size.
Or you can also have a time to live, like I only want these results to stick around for 60 seconds.
It also has a bunch of other interesting features that you can bring in, like works on ORM objects, request objects, a bunch of other things.
It has sort of custom support for custom types that are one of the things that this needs here.
I think it's hashable.
I think you have to have hashable arguments for LRU cache.
Yeah, that strikes me as something very useful.
If you've got a calculation which does some of weights and you want to cache it, then yeah, that looks like a fantastic project.
Not super popular.
People can check it out, but it looks pretty useful.
Okay, we kind of touched on this already, so maybe we won't go too much into it, but just one of the actual lessons you said is immutability is definitely a good thing.
So we can get this from tuples, named tuples, which are like better tuples.
You know, you can address them by a variable type or frozen data classes.
I tend to think immutable first.
So I'll prefer to make my objects immutable.
And only when I think that's going to become a hindrance do I make them mutable.
And that's, I think, I started doing that a few years ago, and I think that's benefited my code.
So I'd recommend mutable objects.
Yeah.
And then if you're out there doing Pydantic, which many of us are these days, it has faux immutability, which is kind of like it.
It takes a shot at making things immutable.
And you can just say allow mutation false as the config for your Pydantic model, which is pretty fantastic.
Yeah.
I think Python doesn't support true immutability.
Immutability.
If you really wanted to, you could do some horrible hack to change an object, which should be immutable.
But, you know, like Python's philosophy is we're all adults here, right?
So don't do crazy things like that.
And even for languages that have like the word const and stuff, it still doesn't necessarily mean what you think it means.
There's a little bit of Monty Python going on.
No, Princess Bride sort of thing going on there where, okay, so this object says it can't be mutable and it has pointers to other objects.
And so sure, you can't reassign those pointers, but that thing points to something which points to something, which internally you call a function which changes.
You know what I mean?
Like to get true immutability is super hard and super restrictive.
Super hard, yeah.
Yeah, yeah.
So Python isn't too bad in that respect.
It's definitely safer than C, C++, et cetera.
Yes, that's for sure.
Okay, immutability is good.
So is Unicode art.
What the heck is this?
In the textual code, there's quite a lot of functions like this, which are kind of geometric.
So it might divide something into two.
Yeah, it's always doing visual things, right?
Quite a lot of visual things.
So here we've got an example.
It's a function which takes a region and splits it into four by making two, or making a horizontal and a vertical cut.
And that's quite hard to express in English succinctly.
I mean, you can do it, but it'll take you like a big paragraph.
But if you create something like this, this kind of Unicode art using various box characters, you can draw a diagram to show what you're doing.
And if you're coming to that code later, that just makes it really obvious at a glance what it's doing.
Yeah, it's fantastic.
I'm a big fan of that kind of Unicode art.
And I use an application called Monodraw, which is macOS only.
But there are similar applications for other platforms.
And yeah, I use it wherever I can.
Also because it's kind of fun.
It is cool.
A powerful ASCII art editor designed for the Mac.
It costs money, $10.
I love that it has educational pricing, but it only costs $10.
That's kind of cool.
But yeah, it's pretty neat.
I guess you have little arrows and boxes.
And just like you might draw with PowerPoint or Keynote or something like that is kind of your, let me put together a visual graphic.
But then output is ASCII art.
Yeah.
And it's kind of like a vector graphic type thing.
So if you draw a box, you can click on the box and drag it.
It's not like a bitmap.
Oh, that's fantastic.
Yeah, it's quite powerful.
I hope the authors made a fortune out of it, frankly, because I love it.
So what is the output like?
You output to clipboard?
Yeah, you can cut and paste it.
You output to clipboard.
You can write it to a text file.
I usually cut and paste it directly into Python code.
Yeah, of course.
So that's really cool.
It also made me think of something that's not exactly the same purpose, but Balsamic.
I don't know if you're familiar with Balsamic, which is really great for developing UIs.
I don't know if they have a gallery or something.
They need some more graphics on this thing here.
Yeah, I've used Balsamic for a while.
It's been around for a long time, hasn't it?
It's been around forever.
Yeah, but it basically lets you create UIs that look almost like as if they were created
using ASCII art.
Not quite, but it's an interesting goal.
I did also want to highlight the most useful piece of ASCII art.
And to be clear, all of this discussion is what goes in the comments, right?
What goes in the code comments or the doc strings?
The one for, what is it, for the object allocator in Python.
So if you look in obmalloc.c in CPython, there's all this fairly intense looking code for allocating
memory when it's particular CPython objects, like a py object or a py long or whatever, right?
If you scroll down to the section here around line 777, it has a paragraph.
It's an object allocator for Python.
Here's an introduction to the layers of Python's memory architecture, etc.
And instead of having an essay in here, it has this incredible graphic that is like vertically aligned and shows you,
here's where we allocate things like integers and lists.
And here's Python core non-object memory allocation.
Here's how it relates down to the OS and to actual hardware.
What do you think of this?
That's pretty awesome.
A lot of respect to the author because I'm fairly sure they didn't use monodraw.
I think they did that by hand with typing the characters and spaces and everything.
Yeah, it's pretty awesome.
Yeah, I think this is really fantastic.
I was trying to piece together, you know, I did a course on Python memory and I was trying to piece together like, well, how do I visually represent how it is described that the memory works?
And I'm like, well, let me go look in the source code.
And I was looking at this like, there's an actual picture in here.
This is amazing.
I can't believe it.
So, so neat.
Yeah.
So anyway, if people need a concrete example of the type of stuff you're talking about, here's one that's pretty close to home.
We all use it every day and we didn't know.
Fractions.
I was always amazed at the fact that 0.1 plus 0.1 plus 0.1 is not equal to 0.3.
I learned that in the school and it was, but for some reason it's not.
What do you think?
Yeah, so this is a problem that goes, this is, it's not just Python.
It's pretty much any language which uses floats and doubles, which is like almost all of them.
And it trips up beginners and experienced developers.
It tripped up me when I was working on Textual.
We have lots of code which will divide the screen into various proportions.
So you might have a third and then a two thirds and you might divide the height into sevenths.
And what I found was that when I used floats, I got a lot of rounding error.
Not, actually not a lot of rounding error, but occasional rounding error, which means it wouldn't draw a line or it wouldn't draw a column.
Because this kind of issue where the rounding error is compounded and it's been rounded down to the nearest character.
Right.
Because you've got to, you've got to talk in like little block pieces anyway.
And if it just misses it, right?
If it just misses it by 0.0000001, then, then you'll be a whole character out.
And I did come up with like various ways of solving this where I used integers rather than floats and kept track of the error, which I think is the standard approach of doing it.
But it was quite tricky code and I get it wrong too frequently.
But then I remembered fractions.
So fractions is, they behave just like numbers, but they start at life as a numerator and a denominator.
So now fraction 1, 2 equals a half.
And the great thing about fractions in the standard library is they don't suffer from that rounding error.
So if you have, you know, three, one tenths, it'll add up to three tenths exactly with no rounding error.
And it makes that kind of code, which I do a lot of in textual, to be much simpler.
So I was very happy when I re-remembered it because I must have known years ago and thought, why do I need fractions?
Yeah, exactly.
Yeah.
This is not elementary school.
I don't need this.
Come on.
We could just say 0.1.
Yeah.
So you learn that you think, well, that's probably useful to mathematicians or something or someone else, not to me.
But if you work long enough, you'll eventually find a problem where that is actually the perfect solution for it.
I didn't know about these either.
I see comments in the audience as well, like, fractions, what is this madness?
So you're telling us that you can only work in rational numbers, not, you know, irrational numbers like pi and E.
We can't have columns that are E-wide.
No, no, you can't.
Or pi high.
It's pi by E.
No, it's cut off.
Darn it.
Yeah.
The cool thing is that fractions, you can drop in replacements to float.
So if you've written some code which was previously using floats and then you pass in fractions, everything will work as normal, except you'll not get the rounding error.
It's really kind of beautiful.
Okay.
So you're saying the fraction library supports things like division, multiplication, additions, and basically would duck typing behave the same?
Yeah.
It's duck typing as a rational number.
So anywhere we use a float, fractional work.
Okay.
I learned about that.
Amazing.
Let's see.
Emojis are hard.
You talked a little bit about things that take up different sizes and Unicode and whatnot.
Yeah.
So when I started Textalize and I got my first employee, I thought this is the problem that I want to tackle because it has been bugging me for two whole years.
And the problem is basically this, that some characters will take up two cells in the terminal, they'll double wide, and some characters will take up one cell.
And if you want to do any kind of formatting, say to draw a line or a panel around it, you need to know exactly how many cells a piece of text will take up.
And it sounds like a simple problem.
All you need to do is know how many cells a character will take up.
So you might have a function which takes a character and returns either one or two.
And in essence, that's what Rich does.
But at some characters, you can't know because they will render differently on different terminals.
They'll render differently on iTerm and Windows and Linux.
And it gets even more complicated.
You can get multiple characters, so multiple code points in the characters.
So you might have something like a flag.
And a flag has a two-letter character code and another character code which tells you this is a flag.
So if you iterate over that, you get three code points.
So you have to know, first of all, how everything works together.
And there's quite a lot of those type of characters.
I can't imagine how tricky it is to be done inside Unicode.
Yeah.
It's crazy.
It's very, very complex.
And, you know, we thought, well, we'll just do it.
We'll just apply some engineering effort and just do it.
But we discovered that it was impossible to know because you can't tell how the terminal is going to render these Unicode.
It might do it correctly.
It might actually render a single character.
If the terminal is not aware of multi-code point emojis, then you might get three characters.
They might render as nothing or they might render as three double width characters.
They might not even render properly.
So they might overlap following character.
Some number of boxes.
Yeah.
It's just every terminal did it differently.
Not only the terminal, it was platform dependent.
So if the terminal used the Unicode database on the operating system, then you'd get different results if it shipped its own copy of the Unicode database.
And it just turned out it was impossible to know how many characters.
So what do you do?
Well, there's a subset up to about Unicode 9 where things seem to be mostly sane, most terminal support.
So if you use those, those are fine.
But after that, it becomes unreliable.
If you have flags and multi-code point characters, then it might not work.
It might cause the alignment of tables and panels to be out.
You know, the end character might be shifted out by one.
And it just comes up so frequently.
And I would solve it if I could.
But as far as I can tell, there is no reasonable solution which will work across all platforms.
Too many unknowns, right?
Too many unknowns, yeah.
It's a crazy thing, but it kind of makes sense because these Unicode characters came out in the last few years.
And operating systems and terminals haven't caught up and they haven't agreed on how to render them.
So it's a frustrating situation.
I'm sure that it is.
Yeah, I mean, if you look at like some of the nerd fonts, for example, when I, you know, at nerdfonts.com, you know, you look at some of these, what is possible.
I think here, you know, you have these like colored arrows.
And do they have, I don't know, if you go over to Oh My Posh, which I don't really have time to talk about.
But if you look at the themes that are in here, the different themes that you can pick and just some of the effects, like a character that looks like a git branch with an arrow in it.
Like, how is that?
How are you supposed to decide how big that is?
Or, you know, how is some of this stuff accomplished?
It's just...
I don't know how they represent those characters.
I wonder if they reuse existing characters.
When I saw that, I'm like, okay, this is...
I don't know how this is possible, right?
Rounded edges and all sorts of stuff.
So I'm like trying to find one where it's like a sparkly fade from one character to the next.
It's these things over in the OhMyPosh.dev themes.
If I thought about your job to figure out, like, what is that supposed to look like?
I don't know.
I would just give up.
Because these are...
I mean, they're really impressive and really useful.
But this one, for example, the cert theme, where it's got like little dots that fade from one to the next.
I just...
I just don't know.
Amazing, but...
Those look pretty cool.
How are you supposed to know, right?
Right.
So those are your lessons.
Very, very cool stuff.
I really appreciate you coming on to talk about the seven lessons.
You know, terminals are fast.
Dictive views are amazing.
LRU cache is fast.
Immutable is good.
Unicode R is good.
Fractions are good.
Emoji is bad.
Does that summarize it?
That about...
Yeah, in a nutshell.
Oh, let's wrap it up with one more thing real quick here.
Let me see if I can find it.
This one?
There we go.
So one of the...
Sort of bring it full circle back around.
One of the cool things to make terminals nicer that you've talked about recently is rich-cli.
You want to close out the show?
Just give us...
Tell us what rich-cli is.
Sure.
Okay.
So as you know, rich is a library.
And rich-cli is a CLI interface for that library.
So most of what rich can do is exposed by rich-cli.
So you can cat most file formats, and it'll be nicely syntax highlighted.
You'll have line numbers and guidelines and all sorts of things.
And you can do things like panels.
You can format text.
What else?
There's a whole bunch of other features.
Yeah.
So for example, if I had a JSON document on the terminal, I could type...
I could open it in some terminal-based editor, you know, think SSH somewhere.
Or I could just type more or cat the name of it, and it would print out just plain text of whatever's on the inside.
Or now I could type rich, the file name, and I get, you know, highlighted, colorized, formatted content for like CSV and JSON and all those kinds of things, right?
Yeah, yeah.
So the rich, the JSON example is quite good because that will decode the JSON.
So if you've got like compressed JSON with no white space, it makes it impossible to read.
Rich will decode it, and it'll also format it.
I see.
Like pretty prints it.
Pretty prints it, exactly.
Yeah.
And it'll do markdown.
It'll do a reasonable job of rendering markdown.
And it'll take CSV files, and it'll turn those into nice rich tables.
And if your output is quite large, you can add hyphen, hyphen, pager.
And that'll put in a nice pager where you can scroll up and down with scroll bars and do page up, page down, et cetera.
So it's kind of like a toolbox for fancy, rich formatting of all sorts of different data types.
Definitely a cool project.
And I know you're concerned about emojis, but Paul in the audience says, fortunately, Doom does not require emojis.
So it's still on the table.
I suppose not, yeah.
That's right.
All right.
Well, fantastic lessons.
Thanks for sharing all of your experience.
Final two questions before you get out of here.
If you're going to write some Python code, work on rich, what editor are you using these days?
It's not so meta that you're using Textual to write Textual yet, is it?
No.
Maybe one day.
But no, I use VS Code.
And I quite like it.
I'm comfortable with it.
My colleague uses, what's the editor by JetBrains?
PyCharm.
And he's very proficient at PyCharm.
And to be honest with you, I am jealous of some of the features of PyCharm.
It does some really cool things.
So I'm kind of tempted to try PyCharm.
Looking over the shoulder.
Awesome.
Yeah.
Very cool.
And then notable PyPI package.
I mean, we've touched on some good ones that start or end with Rich.
But anything else you run across that you're like, oh, this is fantastic.
People should check this out.
Oh, gosh.
There's so many.
I'm drawing a blank.
I should have heard one in advance.
How about one that you use that makes Rich work well or something?
Well, there's Prompt Toolkit.
So I owe Prompt Toolkit a big debt of gratitude.
Because when I was figuring out the textual stuff, I looked at the Prompt Toolkit source
code, which is a great thing about open source that you can look at other people's code.
And it is very good.
It helped me understand things.
And it still does things which Textual doesn't do yet.
So I think Prompt Toolkit is an excellent project.
And if you haven't used it, you should definitely check it out.
Yeah.
Prompt Toolkit's interesting.
And you'll be typing along.
And all of a sudden, there's like a combo drop-down box, like a select right in the middle
of your terminal.
And then you just carry on.
Yeah.
It makes things like, I think it's used in IPython.
It makes that much nicer and much more friendly.
Absolutely.
All right.
Well, final call to action.
People want to do stuff with Rich Textual, maybe take some of these lessons and run with
them.
What do you tell them, Will?
Yeah.
Check out the website.
Check out my Twitter profile.
And if you have any questions, feel free to ping them over to me.
I'm always happy to talk to people about Python-related things.
Yeah.
Fantastic.
And of course, I'll link to the article with your list of topics there so people can check
that out.
Thank you so much for being here.
It's been great to catch up with you.
Yeah.
Thank you.
You bet.
Take care.
Bye.
Bye-bye.
This has been another episode of Talk Python to Me.
Thank you to our sponsors.
Be sure to check out what they're offering.
It really helps support the show.
Starting a business is hard.
Microsoft for Startups, Founders Hub provides all founders at any stage with free resources
and connections to solve startup challenges.
Apply for free today at talkpython.fm/founders hub.
Join Sentry at their conference, Dex, Sort the Madness, the conference for every developer
to join as they investigate the movement and trends for better and more reliable developer
experiences.
Save your seat now at talkpython.fm/Dex.
Want to level up your Python?
We have one of the largest catalogs of Python video courses over at Talk Python.
Our content ranges from true beginners to deeply advanced topics like memory and async.
And best of all, there's not a subscription in sight.
Check it out for yourself at training.talkpython.fm.
Be sure to subscribe to the show.
Open your favorite podcast app and search for Python.
We should be right at the top.
You can also find the iTunes feed at /itunes, the Google Play feed at /play,
and the direct RSS feed at /rss on talkpython.fm.
We're live streaming most of our recordings these days.
If you want to be part of the show and have your comments featured on the air,
be sure to subscribe to our YouTube channel at talkpython.fm/youtube.
This is your host, Michael Kennedy.
Thanks so much for listening.
I really appreciate it.
Now get out there and write some Python code.
I really appreciate it.
Thank you.