Skip to content

Commit 47941e9

Browse files
committed
blog post
1 parent e968ca0 commit 47941e9

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

docs/blog/posts/steal-this-code.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
---
2+
draft: false
3+
date: 2022-11-20
4+
categories:
5+
- DevLog
6+
authors:
7+
- willmcgugan
8+
---
9+
10+
# Stealing Open Source code from Textual
11+
12+
I would like to talk about a serious issue in the Free and Open Source software world. Stealing code. You wouldn't steal a car would you?
13+
14+
<div class="video-wrapper">
15+
<iframe width="auto" src="https://www.youtube.com/embed/HmZm8vNHBSU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
16+
</div>
17+
18+
But you *should* steal code from Open Source projects. Respect the license (you may need to give attribution) but stealing code is not like stealing a car. If I steal your car, I have deprived you of a car. If you steal my open source code, I haven't lost anything.
19+
20+
!!! warning
21+
22+
I'm not advocating for *piracy*. Open source code gives you explicit permission to use it.
23+
24+
25+
From my point of view, I feel like code has greater value when it has been copied / modified in another project.
26+
27+
There are a number of files and modules in [Textual](https://github.com/Textualize/textual) that could either be lifted as is, or wouldn't require much work to extract. I'd like to cover a few here. You might find them useful in your next project.
28+
29+
<!-- more -->
30+
31+
## Loop first / last
32+
33+
How often do you find yourself looping over an iterable and needing to know if an element is the first and/or last in the sequence? It's a simple thing, but I find myself nedding this a *lot*, so I wrote some helpers in [_loop.py](https://github.com/Textualize/textual/blob/main/src/textual/_loop.py).
34+
35+
I'm sure there is an equivalent implementation on PyPI, but steal this if you need it.
36+
37+
Here's an example of use:
38+
39+
```python
40+
for last, (y, line) in loop_last(enumerate(self.lines, self.region.y)):
41+
yield move_to(x, y)
42+
yield from line
43+
if not last:
44+
yield new_line
45+
```
46+
47+
## LRU Cache
48+
49+
Python's [lru_cache](https://docs.python.org/3/library/functools.html#functools.lru_cache) can be the one-liner that makes your code orders of magnitude faster. But it has a few gotchas.
50+
51+
The main issue is managing the lifetime of these caches. The decorator keeps a single global cache, which will keep a reference to every object in the function call. On an instance method that means you keep references to `self` for the lifetime of your app.
52+
53+
For a more flexibility you can use the [LRUCache](https://github.com/Textualize/textual/blob/main/src/textual/_cache.py) implementation from Textual. This uses essentially the same algorithm as the stdlib decorator, but it is implemented as a container.
54+
55+
Here's a quick example of its use. It works like a dictionary until you reach a maximum size. After that, new elements will kick out the element that was used least recently.
56+
57+
```python
58+
>>> from textual._cache import LRUCache
59+
>>> cache = LRUCache(maxsize=3)
60+
>>> cache["foo"] = 1
61+
>>> cache["bar"] = 2
62+
>>> cache["baz"] = 3
63+
>>> dict(cache)
64+
{'foo': 1, 'bar': 2, 'baz': 3}
65+
>>> cache["egg"] = 4
66+
>>> dict(cache)
67+
{'bar': 2, 'baz': 3, 'egg': 4}
68+
```
69+
70+
In Textual, we use a [LRUCache](https://github.com/Textualize/textual/search?q=LRUCache) to store the results of rendering content to the terminal. For example, in a [datatable](https://twitter.com/search?q=%23textualdatatable) it is too costly to render everything up front. So Textual renders only the lines that are currently visible on the "screen". The cache ensures that scrolling only needs to render the newly exposed lines, and lines that haven't been displayed in a while are discarded to save memory.
71+
72+
73+
## Color
74+
75+
Textual has a [Color](https://github.com/Textualize/textual/blob/main/src/textual/color.py) class which could be extracted in to a module of its own.
76+
77+
The Color class can parse colors encoded in a variety of HTML and CSS formats. Color object support a variety of methods and operators you can use to manipulate colors, in a fairly natural way.
78+
79+
Here's some examples in the REPL.
80+
81+
82+
```python
83+
>>> from textual.color import Color
84+
>>> color = Color.parse("lime")
85+
>>> color
86+
Color(0, 255, 0, a=1.0)
87+
>>> color.darken(0.8)
88+
Color(0, 45, 0, a=1.0)
89+
>>> color + Color.parse("red").with_alpha(0.1)
90+
Color(25, 229, 0, a=1.0)
91+
>>> color = Color.parse("#12a30a")
92+
>>> color
93+
Color(18, 163, 10, a=1.0)
94+
>>> color.css
95+
'rgb(18,163,10)'
96+
>>> color.hex
97+
'#12A30A'
98+
>>> color.monochrome
99+
Color(121, 121, 121, a=1.0)
100+
>>> color.monochrome.hex
101+
'#797979'
102+
>>> color.hsl
103+
HSL(h=0.3246187363834423, s=0.8843930635838151, l=0.33921568627450976)
104+
>>>
105+
```
106+
107+
There are some very good color libraries in PyPI, which you should also consider using. But Textual's Color class is lean and performant, with no C dependencies.
108+
109+
## Geometry
110+
111+
This may be my favorite module in Textual: [geometry.py](https://github.com/Textualize/textual/blob/main/src/textual/geometry.py).
112+
113+
The geometry module contains a number of classes responsible for storing and manipulating 2D geometry. There is an `Offset` class which is a two dimensional point. A `Region` class which is a rectangular region defined by a coordinate and dimensions. There is a `Spacing` class which defines additional space around a region. And there is a `Size` class which defines the dimensions of an area by its width and height.
114+
115+
These objects are used by Textual's layout engine and compositor, which makes them the oldest and most thoroughly tested part of the project.
116+
117+
There's a lot going on in this module, but the docstrings are quite detailed and have unicode art like this to help explain things.
118+
119+
```
120+
cut_x ↓
121+
┌────────┐ ┌───┐
122+
│ │ │ │
123+
│ 0 │ │ 1 │
124+
│ │ │ │
125+
cut_y → └────────┘ └───┘
126+
┌────────┐ ┌───┐
127+
│ 2 │ │ 3 │
128+
└────────┘ └───┘
129+
```
130+
131+
## You should steal our code
132+
133+
There is a lot going on in the [Textual Repository](https://github.com/Textualize/textual). Including a CSS parser, renderer, layout and compositing engine. All written in pure Python. Steal it with my blessing.

docs/stylesheets/custom.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,21 @@ body[data-md-color-primary="black"] .excalidraw svg rect {
2424
.excalidraw {
2525
text-align: center;
2626
}
27+
28+
.video-wrapper {
29+
position: relative;
30+
display: block;
31+
height: 0;
32+
padding: 0;
33+
overflow: hidden;
34+
padding-bottom: 56.25%;
35+
}
36+
.video-wrapper > iframe {
37+
position: absolute;
38+
top: 0;
39+
bottom: 0;
40+
left: 0;
41+
width: 100%;
42+
height: 100%;
43+
border: 0;
44+
}

0 commit comments

Comments
 (0)