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.
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!
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.
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.
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.
You can get the notebook itself if you like.
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: