I’m available for freelance work. Let’s talk »

My flexbox layout

Friday 19 April 2024

I recently had to reverse engineer the layout of this site. I created it once upon a time, but had forgotten the details, and to save myself the work five years from now when I have to do it again, I’m noting down what I learned about how it works.

For pedagogical purposes, I made a simplified example of the layout: toystell.html. The layout depends on CSS’s flexbox capabilities. The full implementation on this site includes a responsive layout that changes as the width of the browser shrinks, but the simple example doesn’t include that.

There are five components to the layout: logo, banner, sidebar, content, and footer. The skeleton of the HTML is:

<body>
    <header id="banner">Top Banner</header>
    <main id="content">Content</main>
    <aside id="sidebar">Sidebar</aside>
    <footer id="footer">Footer</footer>
    <header id="logo"></header>
</body>

Notice that the order of the elements in the HTML isn’t the same as the order they appear on the page. Flexbox makes this possible. We start by making the body element a centered flex container:

body {
    max-width: 55rem;
    margin: 0 auto 1rem;
    display: flex;
    flex-flow: row wrap;
}
body > * {
    flex: 1 100%;
}

The container is set to hold flex items in rows that wrap. We’re going to end up with three rows: the banner, the sidebar and content, and the footer. The default flex spec for elements in the body is to take the full width of the container, though this will only apply to the banner and footer.

First the logo is positioned absolutely with a specific height and width. It’s not participating in the flex layout at all, but the other elements will be adjusted to fit neatly around it. It’s square, so its height and width are the width of the sidebar:

:root {
    --side-width: 10rem;
}
#logo {
    position: absolute;
    width: var(--side-width);
    height: var(--side-width);
    background-image: url(https://placehold.co/400/orange/white?text=Logo);
    background-size: contain;
}

Then the banner is added as a fixed-height flexbox item. But if we simply do that, its left edge will be in the same position as the logo’s left edge, and they will overlap. To prevent this and make them abut nicely, we give the banner a left margin equal to the logo’s width:

:root {
    --banner-height: 4rem;
}
#banner {
    order: 1;
    height: var(--banner-height);
    margin-left: var(--side-width);
}

Next comes the fixed-width sidebar. The banner took the full width of the container, so the natural place for the sidebar is just under the banner at the left edge. But we need it lower than that, just under the logo’s bottom edge. To make that work, we give it a top margin of enough to push it down:

#sidebar {
    order: 2;
    flex: 0 var(--side-width);
    margin-top: calc(var(--side-width) - var(--banner-height));
}

All of the flex items were told to take the full width, but the sidebar is our one element that has a fixed width. The top margin is the difference between the height of the logo and the height of the banner.

The content will fit next to the sidebar under the banner just where we want it. “flex: 1 0” means it starts with zero width, but gets all of the remaining space in the flex row, so it’s width grows to take the remaining space:

#content {
    order: 3;
    flex: 1 0;
}

Last comes the footer. It needs a left margin like the banner did so that it won’t overlap the sidebar, and we give the sidebar a negative bottom margin to scooch it up to avoid the height of the footer:

:root {
    --footer-height: 3rem;
}
#sidebar {
    margin-bottom: calc(0rem - var(--footer-height));
}
#footer {
    order: 4;
    margin-left: var(--side-width);
    height: var(--footer-height);
}

I’ve skipped other parts that are in the toy sample, like how each item has carefully chosen borders to form the narrow gutters in the layout. These are just the broad strokes of how the five big boxes negotiate the space in their unusual way. The full site includes responsiveness, and uses Sass compiled to CSS to do some of the heavy lifting.

Better CSS wizards than me probably see better ways to do it, but it works.

Try it: function/class coverage report

Monday 15 April 2024

I’ve added experimental function and class coverage reports to coverage.py. I’d like feedback about whether they behave the way you want them to.

I haven’t made a PyPI release. To try the new reports, install coverage from GitHub. Be sure to include the hash:

$ python3 -m pip install git+https://github.com/nedbat/coveragepy@f10c455b7c8fd26352de#egg=coverage==0.0

Then run coverage and make an HTML report as you usually do. You should have two new pages, not linked from the index page (yet). “htmlcov/function_index.html” is the function coverage report, and the classes are in “htmlcov/class_index.html”.

I had to decide how to categorize nested functions and classes. Inner functions are not counted as part of their outer functions. Classes consist of the executable lines in their methods, but not lines outside of methods, because those lines run on import. Each file has an entry in the function report for all of the lines outside of any function, called “(no function)”. The class report has “(no class)” entries for lines outside of any classes.

The result should be that every line is part of one function, or the “(no function)” entry, and every line is part of one class, or the “(no class)” entry. This is what made sense to me, but maybe there’s a compelling reason to do it differently.

The reports have a sortable column for the file name, and a sortable column for the function or class. Where functions or classes are nested, the name is a dotted sequence, but is sorted by only the last component. Just like the original file listing page, the new pages can be filtered to focus on areas of interest.

You can look at some sample reports:

It would be helpful if you could give me feedback on the original issue about some questions:

  • Is it useful to have “(no function)” and “(no class)” entries or is it just distracting pedantry? With the entries, the total is the same as the file report, but they don’t seem useful by themselves.
  • Does the handling of nested functions and classes make sense?
  • Should these reports be optional (requested with a switch) or always produced?
  • Is it reasonable to produce one page with every function? How large does a project have to get before that’s not feasible or useful?
  • And most importantly: do these reports help you understand how to improve your code?

This is only in the HTML report for now, but we can do more in the future. Other ideas about improvements are of course welcome. Thanks!

Is this for autistic people?

Wednesday 20 March 2024

Special Olympics swimming season started this past weekend. A new athlete joined us, a young boy I’ll call Bryan. He asked me a question that has stuck with me.

Bryan is 12 or so, with the slightly goofy look of a boy growing into his body. He has braces on his too-large teeth. It was his first time swimming with us, so we needed to show him the locker room, how to get out to the pool, and so on. He was serious and inquisitive about all of these things that were new to him.

We got out on the deck and started stretching with the other athletes, most of whom don’t look like Bryan. They are older and have a variety of intellectual disabilities. Bryan surveyed the group then turned to me and asked the question: “is this for autistic people?”

I had only just met Bryan. I didn’t know his formal diagnosis (or if he even had one), and I didn’t know how he thought of himself. When he asked the question, I didn’t know if he was including himself in the category of autistic people or not, so I wanted to answer carefully.

Did he mean, “are autistic people allowed here?” or, “is this only for autistic people?” Maybe he meant, “are all of these swimmers autistic?” or even, “will being here mean I am autistic?”

I told him that it was for autistic people, that my own son Nat was here and Nat is autistic. Bryan accepted this in his sober way and continued on with the practice.

Later I talked with his mom about the question and asked her if Bryan identified as autistic. She said that he did, but it was a recent awareness for him. In school he’s in a typical integrated classroom.

Bryan did well at the practice, and called me “Coach Ned.” His mom was really appreciative of the group as a whole and was clearly pleased.

I’ve been thinking about Bryan and his question: “is this for autistic people?” He’s young, and finding his way in the world in so many ways. We all need to figure out who we are, what groups we belong to, where we fit. We all encounter difficulties in one way or another working all that out, and it’s a life-long process. Bryan has a lot to work on. I hope it isn’t too hard.

Does Python have pointers?

Monday 11 March 2024

People sometimes ask, “Does Python have pointers?” I hate to be the typical senior engineer, but this is one of those questions where the answer is, it depends what you mean by pointer.

The classic definition of a pointer is: a variable that holds the address of something else and that you can use to work with that something else. In very broad pseudo-code, it would be something like this:

myvar = SOMETHING;
mypointer = get_address_of(myvar);
print(get_value_via_pointer(mypointer));
## output is SOMETHING

This is useful because we can use a pointer to refer to data, setting the pointer in one part of the code with whatever logic we need to decide what data it should point to. Then elsewhere we can use the pointer without having to know what it’s referring to or how the decision was made. The pointer gives us an indirection that lets us separate concerns and write more modular code.

Many programming languages provide a pointer facility like this. For example, in C, the get_address_of() operation is ampersand, and the get_value_via_pointer() operation is star, and our code snippet would be:

int myvar = 17;
int *mypointer = &myvar;
print_int(*mypointer);      // outputs 17

Other languages like C++, C#, Go, Rust, Pascal, and even Fortran have similar capabilities.

OK, so what about Python? In one sense, Python doesn’t have a pointer concept like this. You could say that get_address_of() is provided by Python’s id() function, since (in CPython at least) it returns the memory address of the data:

myvar = 17
mypointer = id(myvar)   # ** not useful

But Python has no inverse operation: there’s no get_value_via_pointer() that can get you myvar given mypointer.

So Python doesn’t have the classic pair of operations to be able to work with pointers explicitly. But on the other hand, every variable in Python is a pointer, because variables in Python are names that refer to objects.

In Python, our simple example looks like this:

myvar = 17
mypointer = myvar
print(mypointer)    # outputs 17

When someone asks, does Python have pointers, perhaps the best answer is: it doesn’t have explicit pointers like some other languages, but everything is implicitly a pointer. So you have the power of pointers to use when you need them: you can have multiple data structures, then assign a variable to one you choose, and use the variable later. You’ve achieved the separation of “which data” from “work with the data” that pointers provide.

Maybe this is yet another case of Same words, different meanings.

Note:

  • Some languages like C also allow pointer arithmetic to adjust a pointer from one item in an array to another. Python’s references don’t allow for that.
  • Python’s standard library provides ctypes, which is great for interfacing with native C code, exposing details there including C pointers. This does not count as Python having explicit pointers.

Updated multi-parameter interactive Jupyter notebook

Monday 12 February 2024

A few years ago I wrote Multi-parameter Jupyter notebook interaction about a Jupyter notebook. It worked at the time, but when I dusted it off recently, it didn’t. I’ve renovated it and cleaned it up a little, and now it works again.

It’s a Jupyter notebook with a simulation of late-career money flows to figure out possibilities for retirement. It uses widgets to give you sliders to adjust parameters to see how the outcome changes. It also lets you pick one of the parameters to auto-plot with multiple values, which gives a more visceral way to understand the effect different variables have.

Screenshot of the sliders and resulting plot of outcomes

You can get the notebook itself if you like.

One way to package Python code right now

Saturday 10 February 2024

A year or so ago, I couldn’t find a step-by-step guide to packaging a Python project that didn’t get bogged down in confusing options and choices, so I wrote my own: pkgsample. After I wrote it, I found the PyPA Packaging Python Projects tutorial, which is very good, so I never made a post here about my sample.

Since then, I’ve shown my sample to people a number of times, and they liked it, so I guess it’s helpful. Here’s what I wrote about it back when I first created it:

•    •    •

The Python packaging world is confusing. There are decades of history and change. There are competing tools, with new ones arriving frequently. I don’t want to criticize anyone, let’s just take it as a fact of life right now.

But I frequently see questions from people who have written some Python code, and would like to get it packaged. They have a goal in mind, and it is not to learn about competing tools, intricate standards, or historical artifacts. They are fundamentally uninterested in the mechanics of packaging. They just want to get their code packaged.

There are lots of pages out there that try to explain things, but they all seem to get distracted by the options, asking our poor developer to choose between alternatives they don’t understand, with no clear implications.

I’m also not criticzing the uninterested developer. I am that developer! I don’t know what all these things are, or how they compete and overlap: build, twine, hatch, poetry, flit, wheel, pdm, setuptools, distutils, pep517, shiv, etc, etc.

I just want someone to tell me what to do so my code will install on users’ machines. Once that works, I can go back to fixing bugs, adding features, writing docs, and so on.

So I wrote pkgsample to be the instructions I couldn’t find. It’s simple and stripped down, and does not ask you to make choices you don’t care about. It tells you what to do. It gives you one way to make a simple Python package that works right now. It isn’t THE way. It’s A way. It will probably work for you.

Older: