I'm Mike Pope. I live in the Seattle area. I've been a technical writer and editor for over 30 years. I'm interested in software, language, music, movies, books, motorcycles, travel, and ... well, lots of stuff.

Read more ...

Blog Search

(Supports AND)

Google Ads


Subscribe to the RSS feed for this blog.

See this post for info on full versus truncated feeds.


But though I am much against too much spending, yet I do think it best to enjoy some degree of pleasure, now that we have health, money and opportunity, rather than leave pleasures to old age or poverty, when we cannot have them so properly.

— Samuel Pepys


<April 2018>




Email me

Blog Statistics

First entry - 6/27/2003
Most recent entry - 4/13/2018

Posts - 2491
Comments - 2571
Hits - 2,039,782

Entries/day - 0.46
Comments/entry - 1.03
Hits/day - 377

Updated every 30 minutes. Last: 11:10 PM Pacific

  09:30 PM

I find it harder and harder these days to ride as a passenger when other people drive. Some of that is just part of getting old. But it's not just that; as I've noted before, learning to ride a motorcycle has helped make me a better driver.

One tactic I've worked on is how much space I leave between me and the driver in front of me. To my mind, most people follow too close. You really don't want to do that. Here's why.

Note: If you want the tl;dr, skip to So what do I do?

Speed and distance

Let's start by examining what sort of distance you're covering when you drive, because it might be more than you think. At 10 mph, in 1 second you cover about 15 feet. At 70 mph, in that same 1 second you cover 103 feet. Here's a graphic that illustrates this ratio.

An accepted measure of a car length is 10 feet. This means that if you're going 60 mph, in 1 second you will travel 9 car lengths. Even small differences in speed result in significant differences in how far you travel—the difference between driving at 60 and at 70 is that in 1 second at 70 you will travel an additional 15 feet (88 vs 103). More than one car length.

Braking distance

Let's imagine that for some reason you have to slam on the brakes. Alas, physics tells us that you cannot stop on the proverbial dime. The faster you're going, the longer it takes to get to zero mph.

There's no standard chart for stopping distance for cars. Cars weigh different amounts and they have different brakes; for example, newer cars have anti-lock brakes a.k.a. ABS. (Jeremy Clarkson of the TV show "Top Gear" also makes the case [video] that cars that are engineered to go fast are also engineered to stop quickly.) External factors also come into play, such as the road surface (wet? gravelly?) and the tires on the car. Also whether you're going uphill or downhill.

Anyway, it's complex. The best that people can offer is a minimum braking distance based on a formula that takes into account the initial speed, the car's mass (weight), and the friction coefficient of brakes and road. I'll spare you that here, but I'll suggest some ballpark numbers, as shown in the chart. (You'll see a variety of numbers for this measure if you look around; these are actually on the low end.)

Notice that braking distance at 60 is 180 feet—18 car lengths. That's about 1 city block. Going 70, which of course is only 10 mph faster, the braking distance goes up to almost 250 feet, or an additional 7 car lengths.[1]

Reaction ("thinking") distance

Braking distance is measured from the time the brakes are engaged. But everyone will remind you that there's a delay between the time you see that you have to brake and when you finally get your foot onto the pedal. Remember from the earlier bit that if you delay one second before you hit the brakes, your car might already have traveled many car lengths before you even start slowing down.

The combination of reaction distance and braking distance is referred to as the total stopping distance. Since I'm all about the pretty charts today, here's one from the UK that shows the total stopping distance as "thinking distance" plus braking distance (click to embiggen):

It's the total stopping distance that matters when you're driving behind someone: how far will you keep going before you can stop if something happens ahead of you? Or stated another way, is there enough room between you and the car ahead so that if they slammed on their brakes, you could avoid slamming into them?

As with braking distance, there's no standard measurement for reaction distance. Some people have faster reflexes than others. But even lightning-quick reflexes result in some delay between stimulus and reaction.

And much more importantly, some people pay closer attention to traffic than others. People text while they drive, they yack with their passengers, they watch their GPS, they fool with their radio, they search the passenger footwell for their dropped phone, they watch TV while driving. There have been accidents where the driver behind never hit the brakes at all. In our little car bubble, we sometimes forget that we're piloting 1 ton or more of iron at 88 feet per second, something that's not scary only because we're used to doing it.

And it's your fault

The law generally puts the responsibility on you to leave enough distance. If you rear-end someone, it will almost be always considered your fault. There are a few circumstances when you can make a case that the driver in front contributed to the accident, but the default assumption will be that you were not driving in a safe manner. Not to mention, I suppose, that if you have an accident, you will have had an accident, with all the hassle that that entails.

Think of the traffic

Even if you have superhuman reflexes and a physics-defying vehicle that can stop on a dime, leaving a gap between you and the car in front can have benefits for overall traffic flow. By leaving a gap, you can actually minimize the accelerate-then-brake cycle that characterizes most heavy traffic.

Imagine that you’re behind someone and they tap their brakes to slow down by 5 mph. If you’re close, then you, too, have to tap your brakes to slow down. Even this small slowdown, and even if both of you immediately speed up again, creates a kind of wave that moves backward through traffic. In fact, this is sometimes the source of “phantom” slowdowns on the freeway, where everyone slows down for no apparent reason.

However, if you’re far enough behind someone, when they tap their brakes, you might not need to tap yours at all, or you can slow down enough just by easing off the accelerator. And if you don’t have to, the person behind you doesn’t either, and so on backwards through traffic. Result: possible traffic jam averted.

The engineer William Beatty refers to this as "eating" traffic waves. He explains:
By driving at the average speed of traffic, my car had been "eating" the traffic waves. Everyone ahead of me was caught in the stop/go cycle, while everyone behind me was forced to go at a nice smooth 35MPH or so. My single tiny car had erased miles and miles of stop-and-go traffic. Just one single "lubricant atom" had a profound effect on the turbulent particle flow within miles of "tube."
Obviously, there are limitations to this; if traffic is completely stopped, it’s just completely stopped. But if you can help reduce traffic jams by leaving a little extra space between you and the next person, isn’t that a worthy goal?

So what do I do?

When I got my license many decades ago, the rule was to leave 1 car length ahead of you for every 10 mph. If you're going 60 mph, 6 car lengths. As you can see from the various distances listed above, that's probably not enough. And that's even assuming that you can estimate car lengths correctly, which I think a lot of (most?) people cannot. (Little-known fact: the dashed white lines between lanes on the freeway are 10 feet long.)

When I got my motorcycle license, I learned a far more useful guideline: the three-second rule (PDF). The idea is that when the car ahead of you passes some landmark (a streetlight, a sign, anything static), you count "one-thousand-one, one-thousand-two, one-thousand-three." If you reach the landmark before you finish counting, you're following too close.

The rule is useful because it's independent of speed—the faster you're going, the longer the distance is that you go in 3 seconds, so it (kind of) works out. Naturally, this is just a rule of thumb, which has to take into account all the factors that go into how quickly you can stop. But it has the advantage that if you routinely practice counting off your 3 seconds as you drive on the highway, it means you're paying attention, and that's a very big factor in how safely you're driving.

Event horizon

As if it isn't hard enough just to pay attention to the car in front of, you really should be aware of what's going on in front of them. The same motorcycle manual that recommends the three-second rule suggests that you try to see what's happening 12 seconds ahead of you. This is sound advice, because the driver in front of you might not be paying close attention. If you see that the driver ahead will have to slow down even before they're aware of it, you can adjust your own driving accordingly. (This is captured in the concept of assured clear distance ahead, which you can read about in a particularly poorly written Wikipedia article.)

By the way, all of this applies also to cars behind you, and to your side. Driving safely is hard work.

If you can't see ahead of the car in front of you, it's best to leave even more room than you normally would. I actually have this problem a lot. The motorcycle isn’t very tall, of course. And my normal car is a MINI, which sits pretty low to the ground. In both cases, if I'm behind an SUV or pickup or anything larger than that, I have no clue what's going on up front.

Parting notes

People have pointed out that if you leave gaps ahead of you, other drivers will swoop in. That's true. If you have completely mastered the zen of good following practice, you just let them, and you slow down to again open up a suitable gap ahead of you. I will acknowledge that this is a state of driving enlightenment that most of us can only aspire to. Still, it has helped me to think holistically about safety and about traffic, and if I’m in just the right frame of mind (like, not late to an appointment), I can do a decent impression of someone who actually has mastered all this. And of course, if I'm on the motorcycle, I am ever mindful that even a small miscalculation in how closely I follow can have grave consequences.

For the most part, driving is uneventful. Even if we speed, even if we speed and follow too closely, mostly things don't happen. But it's this very uneventfulness that can make us complacent and lead us to stop paying close enough attention to an activity that can wreak mayhem in the "unlikely event of" a crash.[2]

[1] I'm not sure how clear this is, but the "safety" of a large vehicle like an SUV has to do with how well it survives a crash, not in how well it can avoid one by, for example, stopping quickly.

[2] There's a movement to stop using the word "accident" and instead use the word "crash" in order to emphasize that almost all crashes are caused by drivers. The Motorcycle Safety Foundation puts it this way: "Using the word accident tends to make people think safety is a matter of luck, and it isn't."

[categories]   , , ,

[2] |

  02:36 PM

Here's a keen thing I learned about from a Vox article: a Chrome extension named Library Extension that adds library information when you're viewing books on Amazon or Goodreads. The extension tells you whether the book you're interested in is available in one or more library systems.

Let's say you're interested in the book The ABC of How We Learn, so you look it up on Amazon. In the bar at the top of the page, the library extension icon lights up to tell you that you're on an enabled site:

On the actual page where you're viewing book information, the extension displays library information:

If you want to get the book from the library, you click the Borrow button. This sends you to the library site with the book preloaded.

To configure the extension—for example, to tell it which libraries you want to look in—you click the icon in the toolbar, then click Options:

In the options dialog, you find the library you're interested in, then click the add (+) button:

I just started using this, so I don't know whether I'll end up liking it. It seems a bit intrusive to actually inject information into the page, instead of optionally displaying that information in a dialog or something. I also don't know how robust it is. Does the extension rely on Amazon APIs? Does it scrape information from the page? (A strategy known to be fragile.) How reliable will it be in terms of interacting with library sites?

But I like the concept just fine, since it reflects something I do a lot anyway—namely, look up books, then see if I can get them at the library. I'm curious whether others use this extension, or something like it, and what they think.

[categories]   , ,

[1] |

  09:11 PM

Not long ago I posted something about saving your hearing via the diligent use of earplugs if you are around loud things. (In my case, a motorcycle.) I didn't note then that my problem with being able to hear clearly is not new. I've had trouble for a long time hearing conversation in loud restaurants or understanding dialog in movies.

In fact, I had my hearing tested a while ago. Paradoxically, the results said that my hearing is great in some sort of Platonic sense, as in, when tested in ideal conditions in a lab. But Dr. Ears admitted that there was nothing to be done about my filtering problem—being able to pick out from background noise the sounds I actually wanted to hear.

Hearing aids are an option, I suppose. But good hearing aids are shockingly expensive, and often are not covered by insurance. And it's not at all clear to me that they solve this specific problem of attenuating the background noise specifically.

Well, in the creepy way of modern internet advertising, which can apparently read your mind, I recently started seeing ads for something pretty new: "conversation-enhancing headphones." For example, Bose has a product that they call Hearphones. Doppler Labs (which is suing Bose over all this) has a product they call Here Active Listening headphones. (Here, hear, get it?)

If I understand correctly, the devices combine noise cancellation with directional microphones with a kind of equalizer app (on your phone) to do pretty much what I need, namely tune and/or filter noise versus signal. And all at a price that is significantly less than hearing aids (Bose: $600, Doppler: $300). It's true, of course, that you're wearing headphones versus the invisibility of hearing aids. Then again, wearing headphones is pretty normal in a lot of contexts.

I'm pretty excited by all this. In fact, I'd probably go ahead and get a pair of these, but I've been around technology long enough to know that it's not usually a good idea to get version 1 of anything. By the time v3 of these things is available, they should be pretty great, right?

Let me add a linguistic note here as well. The name that Bose has come up with—hearphones—strikes me as so perfect for this device that it feels like it could easily become the genericized term for this class of thing. Any bets?

[categories]   ,

[1] |

  12:24 PM

I rassled a bit recently with a couple of dumb issues when creating some Word macros, so I thought I'd better write these up for my own future reference. To be clear, "dumb" here means that I should already have known this stuff, and I wasted time learning it.

1. Calling subroutines

I was trying to call a sub like this:
Sub SomeMacro
SomeOtherSub(p1, p2)
End Sub
Word got so mad about that SomeOtherSub call:

Turns out that when you call a subroutine in VBA and pass parameters, you do that without parentheses:
SomeOtherSub p1, p2
The parameters can be positional, as here, or named. For the latter, use the := syntax:
SomeOtherSub p1:="a value", p2:="another value" 

2. Exposing subroutines (implicit access modifiers)

Here was another kind of bonehead mistake I made. I wrote a subroutine sort of like this:
Sub MyMacro(param1 As String, param2 As String)
' Code here
End Sub
Then I tried to actually run this macro (Developer > Macros). The macro stubbornly refused to appear in the Macros dialog box. If I was in the macro editor and pressed F5 to try to launch it in the debugger, Word just displayed the Macros dialog box for me to pick which macro to run, but again, did not display the actual macro that I actually wanted to run.

Anyway, long story short (too late, haha), the problem was that the Sub definition included parameters:
Sub MyMacro(param1 As String, param2 As String)
Apparently if a subroutine has parameters like that, VBA considers it to have protected access—it can be called from another macro, but it can't be launched as a main. This makes sense, but it wasn't immediately obvious. What I really wanted was this:
Sub MyMacro()
I had included the parameters by accident (copy/paste error), so it was basically a dumb mistake. I just removed them and then things worked. Well, they worked until VBA ran into the next dumb mistake, whatever that was. (In my code there's always another one.)

[categories]   ,


  02:35 PM

Another quick post about Word, primarily for my own benefit (when I forget this later).

Word has several options for how you can paste text:

They are (in order):
  • Keep Source Formatting. This option keeps the original formatting (both character and paragraph formatting), but converts it to direct formatting.

  • Merge Formatting. This option copies basic character formatting (bold, italics, underline) as direct formatting, but does not copy any paragraph formatting.

  • Use Destination Styles. This option copies the text and applies styles that are in the target document. (This option appears only if there matching styles in the target doc.)

  • Keep Text Only. This option copies the text as plain text, with no formatting.
I need the last one (paste plain text) more often than any of the others, so I want it on a keyboard shortcut. You can do this by recording a macro of yourself using the Keep Text Only option. But I realized there's an even easier way—just assign a keyboard shortcut to the built-in PasteTextOnly command.

I keep forgetting that most anything Word can do has a command. If a gesture requires just one command, you can assign a keyboard shortcut directly to it. Maybe writing this out will help me remember.

Update I added a video!

[categories]   , ,


  12:01 AM

This is another in a series of blog posts about how I configure Microsoft Word, which I add here primarily for my own reference.

I often use the Style pane, and within that pane, I often want to change the styles that are displayed. Sometimes I want to see all the styles; sometimes just the styles that are defined in the current document; sometimes just the styles currently in use.

You can change this display by using a dialog box. In the Styles pane, click the Options link, and then use the dropdown lists to select which styles to display and how they're ordered, like this:

But that can get to be an annoying number of clicks if you're switching between these display options frequently. So, macros to the rescue. I recorded myself making one of these changes, then created a couple of variations to give me the different displays I want. Here are the macros I currently use, where the sub name is (I hope) self-explanatory:
Sub SetStylesPaneToAllAlphabetical()
ActiveDocument.FormattingShowFilter = wdShowFilterStylesAll
ActiveDocument.StyleSortMethod = wdStyleSortByName
End Sub

Sub SetStylesPaneToInCurrentDocument()
ActiveDocument.FormattingShowFilter = wdShowFilterStylesAvailable
ActiveDocument.StyleSortMethod = wdStyleSortByName
End Sub

Sub SetStylesPaneToInUse()
ActiveDocument.FormattingShowFilter = wdShowFilterStylesInUse
ActiveDocument.StyleSortMethod = wdStyleSortByName
End Sub
To complete the picture, I map the macros to these keyboard shortcuts:


[categories]   , ,


  12:23 AM

I have used Microsoft Word for years—decades—but hardly a week goes by when I don't learn something new. (Including things that are probably pretty well known to others, oh well.) Anyway, TIL about how to use the batch version of auto-formatting in Word. Since I think a lot of people already know this, I'm adding the information here primarily for later reference for myself.

Word has settings to perform "auto-formatting as you type." These include things like converting quotation marks into so-called smart quotes (i.e., typographical quotation marks), converting double hyphens (--) into em-dashes (—), converting typed fractions (1/2) into typographic fractions (½), etc. You set these options in the AutoCorrect dialog box: File > Options > Proofing, AutoCorrect Options button, AutoFormat As You Type tab.

It turns out that Word can also apply these auto-formatting instructions after the fact. In the same AutoCorrect dialog box, there's a tab named just AutoFormat:

This has most of the same options as with auto-format-as-you-type. Here's the neat part: you can get Word to apply these formatting options by pressing alt+ctrl+k. There's no UI gesture, but you can use the feature for customizing the ribbon to add the relevant command to the ribbon or Quick Access Toolbar.

A use case where I can see this working pretty well is if you paste text in from a text editor. (I do this all the time.)

Credit where it's due: I learned about this from the article How to Automatically Format an Existing Document in Word 2013 by Lori Kaufman on the How-To Geek site. As I say, I'm adding this info here primarily for my own benefit. :-)

[categories]   , ,


  09:04 AM

I was reading a thread on a computer forum, and someone asked this question:
Your password should contain at least 6 characters

If you're going to require it; don't say "should", say "must".
This set off an interesting discussion on the semantics of should in this context. I've written about this before, so I was interested to hear how people interpreted the example.

Here is a sampling of the more serious posts on the thread:

From the requirements document: "The password entered by the user should be rejected if it does not contain at least six characters." If I received that requirement from my boss, I would make darn sure that the password is rejected. I don't think I would randomly reject some and not others.

The software is being polite; it's anticipating users who do not like being told what to do.

If it says "should" then it is not optional, like in "could". You should be "this tall" to ride this ride.

A number of people pulled out dictionary definitions (Wikitionary, heh). And one person cited RFC 2119 ("Key words for use in RFCs to Indicate Requirement Levels"), which states:

MUST This word, or the terms "REQUIRED" or "SHALL", mean that the definition is an absolute requirement of the specification.

SHOULD This word, or the adjective "RECOMMENDED", mean that there may exist valid reasons in particular circumstances to ignore a particular item, but the full implications must be understood and carefully weighed before choosing a different course.

All of which goes to the original poster's point—the message was ambiguous and should (ha) have been written with must. For those of us who don't keep a mental catalog of RFC recommendations, the more accessible Microsoft style guide says:

Use should only to describe a user action that is recommended, but optional. Use must only to describe a user action that is required.

In documentation, in error messages, in any context where the message needs to be clear and you aren't there to help the reader understand, avoid should when you mean must.

[categories]   , , ,


  09:30 PM

I found an interesting intersection recently of two things I think about a lot. One is traffic, a topic of perennial interest to me. The second is data visualizations, something that I'm comparatively new to but very interested in.

Let me back up slightly. Not long ago (maybe in 2013?), the state of Washington introduced variable speed limits in a some areas that are prone to congestion, like on I-5 northbound approaching Seattle:

[source, license, cropped slightly and reduced]

I was traveling with someone (my daughter, I think) who asked "Does that work?" To which my answer was that it could, if people actually obeyed these variable limits[1]. (Which they don't at all.) What's the theory?

On their website, the state explains variable speed limits this way:
Ideally, approaching traffic will slow down and pass through the problem area at a slower but more consistent speed reducing stop and go traffic. By reducing stop and go traffic we’re also reducing the probability of an accident by giving drivers more time to react to changing road conditions. This helps drivers avoid the need to brake sharply as they approach congestion.
Hmmm. This sort of describes the theory, but only in general terms. I also found the following on a different state site, which explains the theory even less, but does include a curious bonus reason (emphasis mine) for variable speed limits:
Variable speed limits offer considerable promise in restoring the credibility of speed limits and improving safety by restricting speeds during adverse conditions.
So let me give it a shot. Imagine that you want to go to the movies. You go to the ticket booth and buy tickets. Let's say that this transaction takes 30 seconds. Just as you finish, someone else walks up to buy their ticket. Just as they finish their 30-second transaction, a third person walks up, and so on. As long as people don't arrive at the ticket booth any more frequently than every 30 seconds, there's never a line.

But let's say that 15 seconds after you started buying your tickets, someone gets in line behind you. That person has to wait 15 seconds. And let's say people arrive at the ticket booth every 15 seconds from then on, but the ticket vender can't go any faster than one transcation per 30 seconds. The result is that the line grows, and it continues to grow as long as people arrive at the queue faster than they can buy tickets. The ticket booth is a bottleneck, and the queue is congestion.

Make sense? Congestion results from people being added to a queue (or otherwise approaching a bottleneck) faster than they can leave it. This is as true for people buying movie tickets as it is for cars approaching a slowdown. If you can prevent people from joining the queue faster than they can leave it, you can reduce the delay. If you're selling movie tickets, I don't know how you prevent people from getting in line. But if you're managing traffic, you can try to keep people out of the congestion by slowing down how fast they get to the point where the slowdown occurs.

Some people have understood this for a long time, and voluntarily slow down when it looks like traffic is heavy ahead. William Beaty has a great article (undated?) in which he dives deep on ways that even a few drivers who behave intelligently in congestion and during merges can improve flow for everyone. And while his suggestions undoubtedly work, they rely on people engaging in non-intuitive behavior, like allowing people to merge (gasp!) and leaving long-ish gaps ahead of them.

Since most people don't have the benefit of Beaty's insights, the state has decided to try variable speed limits: if people won't regulate their own speed in reaction to congestion ahead, the state (the state's computers) will attempt to do it for them.

This brings me to the visualization part of our story. Lewis Lehe is a graduate student in transportation engineering who's created a beautiful, interactive visualization that illustrates bottlenecks. (The viz is actually about the difference between bottlenecks, which I'm interested here, and gridlock.) Lehe's visualization shows cars arriving at and leaving a bottleneck, and you can adjust the arrival rate to see interactively how congestion grows if cars arrive faster than they can leave (or vice versa). Click the link and then play with the viz to get a great sense of how variable speed limits could work.

An interesting promise of self-driving cars, like the one apparently forthcoming from Google, is that they could be a whole lot smarter than human drivers about driving in congested conditions. Assuming, of course, that humans aren't allowed to take control of a car that's driving—per their own sense—exasperatingly slow. That remains to be seen.

[1] In fact, the minimum speed (perhaps by federal law?) is 40 MPH, so there's definitely a bottom limit to when variable speeds could be effective.



  09:52 AM

Carrying on with adventures using the Tumblr API. (Part 1, Part 2)

As noted, I decided that I wanted to create a local HTML file out of my downloaded/exported Tumblr posts. In my initial cut, I iterated over the list of TumblrClass instances that I'd assembled from the downloaded posts, and I then wrote out a bunch of hard-coded HTML. This worked, but was inflexible, to say the least—what if I wanted to reorder items or something?

So I fell back on yet another old habit. I created a "template" of the HTML block that I wanted, using known strings in the template that I could swap out for content. Here's the HTML template layout, where strings like %%%posttitle%%% and %%%posturl%%% are placeholders for where I want the HTML to go:
<!-- tumblr_block_template.html -->
<div class="post">
    <div class="posttitle">%%%posttitle%%%</div>
    <div class="postdate">%%%postdate%%%</div>
    <div class="posttext">%%%posttext%%%</div>
    <div class="postsource">%%%postsource%%%</div>
    <div class="posturl"><a href="%%%posturl%%%"
    <div class="postctr">[%%%postcounter%%%]&nbsp;
        <span class="posttype">%%%posttype%%%</span>
The idea is to read the template, read each TumblrClass item, swap out the appropriate member for the placeholder, and build up a series of these blocks. Here's the code to read the template and build the blocks of content:
html_output = ''
html_file = open('c:\\Tumblr\\tumblr_block_template.html', 'r')
html_block_template = html_file.read()
ctr = 0
for p in sorted_posts:
    new_html_block = html_block_template
    ctr += 1
    new_html_block = new_html_block.replace('%%%posttitle%%%', p.post_title)
    new_html_block = new_html_block.replace('%%%postdate%%%', p.post_date)
    new_html_block = new_html_block.replace('%%%posttext%%%', p.post_text)
    new_html_block = new_html_block.replace('%%%postsource%%%', p.post_source)
    new_html_block = new_html_block.replace('%%%posturl%%%', p.post_url)
    new_html_block = new_html_block.replace('%%%postcounter%%%', str(ctr))
    html_output += new_html_block
To embed these <div> blocks into an HTML file, I did the same thing again—I created a template .html file that looks like this:
<!-- tumblr_template.html -->
  <link rel="stylesheet" href="tumbl_posts.css" type="text/css">
  <meta http-equiv="content-type" content="text/html;charset=utf-8">
<h1>Tumblr Posts</h1>
With this in hand, I can read the template .html file and do the swap thing again, and then write out a new file. To actually write the file, I generated a timestamp to use as part of the file name: 'tumbl_bu-' plus %Y-%m-%d-%H-%M-%S plus '.html'.

There was one complication. I got some errors while writing the file out, which turned out to be an issue with Unicode encoding—apparently certain cites that I pasted into Tumblr contain characters that can’t be converted to ASCII, which is the default encoding for writing out a file. The solution there is to use the codecs module to convert. (It’s possible that this is a problem only in Python 2.x.)

Here’s the complete listing for the Python script. (I wrapped some of the lines in a Python-legal way to squeeze them for the blog.)
import datetime,json,requests
import codecs # For converting Unicode in source

class TumblrPost:
def __init__(self,
self.post_url = post_url
self.post_date = post_date
self.post_text = post_text
self.post_source = post_source
self.post_type = post_type
if post_title is None or post_title == '':
self.post_title = ''
self.post_title = post_title

all_posts = [] # List to hold instances of the TumblrPost class
html_output = '' # String to hold the formatted HTML for all the posts
folder_name = 'C:\\Tumblr\\'

# Get the text posts and add them as TumblrPost objects to the all_posts_list
print "Fetching text entries ..."
request_url = 'http://api.tumblr.com/v2/blog/mikepope.tumblr.com/posts/text?api_key=[MY_KEY]'
offset = 0
posts_still_left = True
while posts_still_left:
request_url += "&offset=" + str(offset)
print "\tFetching text entries (%i) ..." % offset
tumblr_response = requests.get(request_url).json()
total_posts = tumblr_response['response']['total_posts']
for post in tumblr_response['response']['posts']:
# See https://www.tumblr.com/docs/en/api/v2#text-posts
p = TumblrPost(post['post_url'],
post['body'], '',
'text') # No source for text posts
offset += 20
if offset > total_posts:
posts_still_left = False

# Get the quotes posts and add them as TumblrPost objects to the all_posts_list.
print "Fetching quote entries ..."
request_url = 'http://api.tumblr.com/v2/blog/mikepope.tumblr.com/posts/quote?api_key=[MY_KEY]'
offset = 0
posts_still_left = True
while posts_still_left:
request_url += "&offset=" + str(offset)
print "\tFetching quote entries (%i) ..." % offset
tumblr_response = requests.get(request_url).json()
total_posts = tumblr_response['response']['total_posts']
for post in tumblr_response['response']['posts']:
# See https://www.tumblr.com/docs/en/api/v2#quote-posts
p = TumblrPost(post['post_url'],
post['source'], '',
'quote') # No title for quote posts
offset += 20
if offset > total_posts:
posts_still_left = False

sorted_posts = sorted(all_posts,
key=lambda tpost: tpost.post_date,

print "Creating HTML file ..."

# Read a file that contains the HTML layout of the posts,
# with placeholders for individual bits of data
html_file = open(folder_name + 'tumblr_block_template.html', 'r')
html_block_template = html_file.read()

ctr = 0
for p in sorted_posts:
new_html_block = html_block_template
ctr += 1
new_html_block = new_html_block.replace('%%%posttitle%%%', p.post_title)
new_html_block = new_html_block.replace('%%%postdate%%%', p.post_date)
new_html_block = new_html_block.replace('%%%posttext%%%', p.post_text)
new_html_block = new_html_block.replace('%%%postsource%%%', p.post_source)
new_html_block = new_html_block.replace('%%%posturl%%%', p.post_url)
new_html_block = new_html_block.replace('%%%postcounter%%%', str(ctr))
new_html_block = new_html_block.replace('%%%posttype%%%', p.post_type)
html_output += new_html_block

# The template has a placeholder for the content that's generated dynamically
html_file = open(folder_name + 'tumblr_template.html', 'r')
html_file_contents = html_file.read()
html_file_contents = html_file_contents.replace('%%%posts%%%', html_output)

# Open (i.e., create) a new file with the ability to write Unicode.
# See http://stackoverflow.com/questions/934160/write-to-utf-8-file-in-python
file_timestamp = datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
with codecs.open(folder_name +
'tumbl_bu-' +
file_timestamp +
'.html', 'w', "utf-8-sig") \
as new_html_file:

print 'Done!'