Passwords, widgets, and Pyto

When I switched from 1Password to Apple’s built-in password manager, I had concerns about the reliability of Apple’s system, but overall I’ve been pleased with how things have worked out. There’s just one problem I’ve run into. It doesn’t show up often, but the annoyance it causes when it does show up has led me to build a workaround.

When you need a new password, iOS offers to create one for you.

iOS password notice

This notice appears when you put the cursor in a password field, and the field itself gets highlighted with part of the strong password iOS made.

Password field with new strong password

If all goes well, your new login works and all the details are saved in your iCloud Keychain for later use. But sometimes the website’s logic and Apple’s don’t mesh, and your new credentials aren’t saved. Now you have an account for which you don’t know the password. Lovely.

The obvious solution would be to copy the iOS-created password to the clipboard before tapping the button that creates the new account. That way, if iCloud Keychain doesn’t save the new credentials, you can add them directly. Unfortunately, iOS doesn’t allow you to copy the password field when the strong password it created is in there. So you have to go the “I forgot my password” route with the website to get a new new password to replace the old new password you just created a few seconds ago. I always feel like the website is judging me when I have to do this.

Presumably, Apple refuses to let you copy the password it just made because there’s a security risk to having a password on the clipboard. But there’s also a security risk to having a password manager that doesn’t save your password.

To get around this problem, I wrote a script to generate passwords for me using a variant of the Diceware method. It was written in Pythonista, and used lists of words that I’d gathered for my Countdown anagram script. It did a good job of generating passwords, but because Pythonista doesn’t work well with Shortcuts, it was clumsy to use.

Enter Pyto, an iOS Python app that does work well with Shortcuts, and iOS 14 widgets, which make it easier than ever to run Shortcuts without digging into the Shortcuts app itself.

Now I have a medium-sized Shortcuts widget with four shortcuts.

Shortcuts widget

Tapping the New Password button puts a new password on my clipboard. I can then paste this into the password field on the website where I’m creating a new account. If the credentials get saved to iCloud Keychain, great. If the credentials don’t get saved to iCloud Keychain, the password is still there on my clipboard, and I can use it to add a new login manually to the Passwords section of Settings. Either way, I then copy something else to the clipboard to overwrite the password.

The New Password shortcut is just a call to run a Pyto script:

New Password shortcut

(The comment is there mainly to force Shortcuts to use the icon I chose. I’ve noticed that single-step shortcuts often get displayed with the icon of the step rather than the icon of the shortcut itself. Before I added the comment, the widget button for this shortcut was shown with the Pyto icon instead of the key icon.)

The Pyto script is pretty simple:

python:
 1:  from secrets import choice, randbelow
 2:  import pasteboard
 3:  
 4:  # Get a list of words that are 4-6 characters long.
 5:  with open('words.py') as f:
 6:    words = f.readlines()
 7:  
 8:  # Select 4 words from the list.
 9:  pwords = [ choice(words).strip() for i in range(4) ]
10:  
11:  # Add a numeral and a "special" character.
12:  # Capitalize one of the words.
13:  numeral = str(randbelow(10))
14:  special = choice('!@#$%^&*')
15:  pNum = randbelow(4)
16:  pSpec = randbelow(4)
17:  pCap = randbelow(4)
18:  pwords[pNum] = pwords[pNum] + numeral
19:  pwords[pSpec] = pwords[pSpec] + special
20:  pwords[pCap] = pwords[pCap].capitalize()
21:  
22:  # Assemble the password and put it on the clipboard.
23:  pw = '-'.join(pwords)
24:  pasteboard.set_string(pw)

Note first that this script uses the relatively new secrets module instead of random to generate random numbers. The secrets module was built specifically to use in cryptographic applications like this.

The words.py file that’s opened and read in Lines 5–6 is a text file consisting of about 28,000 words that are 4–6 letters long, one per line. It’s not a Python source code file, but I had to give it a .py extension to save it in Pyto’s Documents directory along with the scripts. Pyto really should be less strict about what file types can be saved there.

Line 9 selects four words from the list. Lines 13, 15, and 18 add a numeral to the end of one of the words. Lines 14, 16, and 19 add a “special” character to one of the words. Lines 17 and 20 capitalize one of the words. The additions and capitalization are done because some websites won’t accept passwords without them.

Finally, Line 23 puts the words together with hyphens and Line 24 copies the result to the clipboard. Here’s an example of the kind of password it generates:

rideau4-tawery-conure&-Rewin

You can tell that the word list I used is based on a Scrabble-type dictionary and not a common-words dictionary. I keep my conure’s cage behind a rideau in the tawery.

With this widget on my iPhone and iPads, I can avoid the occasional hiccups of iCloud Keychain without digging through lists of shortcuts or scripts. A good password is always just one tap away.

Update Oct 5, 2020 10:52 AM
I needed to make a couple of changes to get this working consistently across all my devices.


What is Notes good for?

A few months ago, the Mac Power Users podcast had an episode on note-taking applications in which David and Stephen talked about how they use different notes apps for different kinds of notes. As I listened, I realized that I’ve been sorting my note-taking into two apps, Drafts and Notes, and have been using a fairly simple strategy for deciding between the two.

Before I get into the strategy, I want to congratulate the Notes team for making what may be the only Apple app that has been unambiguously improving over the past few years. While it still shows evidence of the limited and ugly app it started out as, Notes is now one of the best note-taking apps on iOS, with features that nicely balance simplicity and power.

On iOS, I generally start writing notes in Drafts. They get moved into Notes if any of the following apply:

“Need” may be too strong a word for the first two criteria, but I have several notes that are so much more useful to me because of their formatting, I would never want them to go back to plain text.

Here’s an example: For almost as long as I’ve had an iPhone, I’ve kept on it an edited version of the commuter train schedule between Chicago and Naperville, where I live. This started as a set of bookmarks to static web pages I built. It then shifted to plain text, which I kept first in Simplenote, then Elements, and then Drafts.1 Sometime in the past year, I moved it to Notes.

There are six different schedules, which depend on the day of the week and the direction of the train. I keep them in a folder named Metra (which is the name of the rail line).

Metra folder

Each note is a two-column table with the times for the Naperville stop and the Union Station stop.

Metra eastbound schedule

The tabular formatting makes the schedule easier to read than the old plain text versions. And I’m using bold and italic to denote the express trains (which have fewer intermediate stops and therefore run faster). I’d prefer to leave the text style alone and change the background color of those rows, but that’s not an option in Notes.

Of course, Metra has an app and a website for all of this information, but it’s so much faster to have it in a note that opens quickly and contains only the schedule parts I care about.

The train schedule is permanent. The schedules may change, but I’ll always want them. Less permanent are a set of book lists I made this year to keep track of what I own and what I’ve read.

I’ve been reading a lot of mysteries lately, and they tend to come in series. I have a Books folder with the series I’m reading.

Book lists

Each series is presented as a checklist.

Nero Wolfe checklist

The circular checkboxes provided by Notes tell me whether I own a book or not. The text checkmarks after the titles (✔︎) tell me whether I’ve read it. The second check is kind of a kludge, but I have a text replacement (chz) defined to make it easy to type the checkmark.

As with the train schedule, this could be a plain text note, but it’s more convenient and easier to read with formatting.

Although these lists are long-lasting, I don’t consider them permanent. When I’m done reading a series, I’ll delete its list. The Brother Cadfael list is one book away from being deleted.

I know many of you will suggest Goodreads for this kind of tracking. But I’m not really interested in all the extras that Goodreads provides, and don’t want to mess with a website or another app just to keep track of simple lists. Notes is a good fit for my needs.

As for personal information, I have locked notes for software licenses, car registrations and licenses, insurance information, contact lens and glasses prescriptions, and so on. Nothing that’s super sensitive, just stuff that I think should be somewhat protected. It’s material I used to keep in 1Password and wouldn’t feel comfortable keeping in Drafts.

I opened Notes the other day and was surprised at how many notes I have in there. My experience parallels David’s and Stephen’s—it’s gone from an app I had no use for whatsoever to one that I open nearly every day.


  1. There may have been another app in there between Elements and Drafts, but I don’t remember it. 


Weighing in

In yesterday’s post, I mentioned that it took me a year to finally get around to writing a script I’d been thinking about. Today’s post is one whose seed was planted almost two years ago.

In October 2018, Myke Hurley and Stephen Hackett were in Chicago for a Relay FM event that I attended. During the event, they recorded this episode of Ungeniused, their podcast about weird articles on Wikipedia. In honor of the city they were in, the article they chose was “Raising of Chicago,” which describes how, in the 1850s and 1860s, the roadways and buildings of the city were elevated as much as six feet to get them up out of the muck and allow decent drainage of both stormwater and wastewater.

The roadways were easy because you don’t really lift a street; you just add a bunch of fill and build a new road on top of that. But substantial buildings had to be jacked up to keep their ground floors from becoming basements. Here’s an image from the article of a large team of men doing just that.

Briggs House image from Wikipedia

One of the advantages of blogging over podcasting is you get to include cool pictures like this.

As they were describing the process, Myke and Stephen mentioned the weights of some of the buildings that were lifted, and Stephen asked, “How do you estimate the weight of a building?” After the recording, I told him that the weights of significant buildings are always known by the people who build them. I further said that I would write a post about it. And to prove that I’m a man of my word, here I am… two years later.

(Another advantage of blogging—at least if you don’t have advertisers to satisfy—is that you’re not on a schedule.)

There are two good reasons architects and engineers know the weights of the buildings they design: economic and structural.

The economic reason is simple. Buildings are made primarily from commodity products—things that are sold by weight or volume. When you buy the steel, concrete, stone, masonry, wood, plaster, tile, glass, etc. that your building is made from, you are, in effect, buying your building by the pound. That was as true in 1850 as it is today.

The structural reason is nearly as simple as the economic one. The building has to stand up, which means that each part has to carry both its own weight and the weight of all the parts it supports. The top floor has to carry its own weight and that of the roof. The next floor down has to carry its own weight and that of the top floor and roof. And so on down the building. Ultimately, the entire weight of the building and its contents passes through the foundation and into the soil. The designer has to know what that weight is in total and how it’s distributed among the columns and walls of the building; otherwise, the foundation will crack or sink into the soil.

In fact, what you need to know about the weights and their distribution in order to design the building is exactly what you need to know if you want to jack that building up. The parts of the foundation that had to be extra large or extra strong because they carry more weight will also need more or larger jacks to lift it.

It can be difficult in older buildings to know what all these weights are because the plans have been discarded or misplaced. But that wasn’t a problem in the Chicago of 1850s. There simply were no old buildings back then—not any of significant size, anyway. Chicago was a very young city.

In summary, knowing the weight of a building goes hand in hand with designing it, because it’s a critical part of the building’s cost and its ability to stand up.

So there you go. Better late than never. Speaking of which…

If you’re like me and put things off, you haven’t made your donation to St. Jude’s Children’s Research Hospital yet. You’ve been hearing about it, of course, on the podcasts you listen to, and you’ve told yourself you’re going to do it. But while September is getting long in the tooth, it’s not over yet, and your donation will still help the families that rely on St. Jude’s and still count toward Relay’s fundraising goals for the month.

Stephen will appreciate your donation even more than knowing how much a building weighs.


Running numbers

About a year ago, I wrote a post comparing jot and seq, two utilities for generating sequences of numbers. They differ in the ordering of their arguments and in some of the options available for formatting the numbers. The upshot was that I liked some parts of jot, some parts of seq, and some parts of neither. The unwritten conclusion of the post was that I should write my own sequence-generating command with arguments and options tuned to the way I think and work. I’ve finally gotten around to doing that.

Here’s the code for run, a simple Python script that does the same simple task that jot and seq do, but with arguments ordered in a way that makes sense to me.

python:
 1:  #!/usr/bin/env python
 2:  
 3:  import docopt
 4:  import codecs
 5:  
 6:  usage = '''Usage:
 7:  run [options] <stop>
 8:  run [options] <start> <stop>
 9:  run [options] <start> <stop> <step>
10:  
11:  Generate a run of integers or characters. Similar to jot and seq.
12:  
13:  Options:
14:    -f FFF   formatting string for number
15:    -s SSS   separator string
16:    -c       characters instead of integers
17:    -r       reverse the run
18:    -h       show this help message
19:  
20:  The run of numbers can be integers or reals, depending on the values of start, stop, and step. The defaults for both start and step are 1. If -c is used, then start and stop must both be given as characters and step (if given) is an integer.'''
21:  
22:  # The arguments for -f and -s come in as raw strings, but we
23:  # need to be able to interpret things like \t and \n as escape
24:  # sequences, not literals.
25:  def interpret(s):
26:    if s:
27:      return codecs.escape_decode(bytes(s, 'utf8'))[0].decode('utf8')
28:    else:
29:      return None
30:  
31:  # Handle the command line options and arguments.
32:  args = docopt.docopt(usage)
33:  fstring = interpret(args['-f']) or '{}'
34:  sep = interpret(args['-s']) or '\n'
35:  rev = args['-r']
36:  char = args['-c']
37:  step = int(args['<step>'] or 1)
38:  
39:  # The interpretation of start and stop depend on -c
40:  if char:
41:    start = ord(args['<start>'])
42:    stop = ord(args['<stop>'])
43:  else:
44:    start = int(args['<start>'] or 1)
45:    stop = int(args['<stop>'])
46:  
47:  # Generate the run as a list of integers.
48:  # Include stop if it fits the sequence.
49:  run = list(range(start, stop, step))
50:  if run[-1] + step == stop:
51:    run += [stop]
52:  
53:  # Convert to text
54:  if char:
55:    runText = [ fstring.format(chr(n)) for n in run ]
56:  else:
57:    runText = [ fstring.format(n) for n in run ]
58:  
59:  # Reverse the list if asked.
60:  if rev:
61:    runText.reverse()
62:    
63:  print(sep.join(runText))

As you can see, run takes one, two, or three arguments. If given one argument, it prints the integers from 1 to that number, e.g.,

run 5

produces

1
2
3
4
5

This is just like jot and seq with a single argument.

With two arguments, run prints the integers from the first through the second, e.g.,

run 4 9

produces

4
5
6
7
8
9

This is just like seq, but very different from jot, which would need

jot 6 4

to produce the same list of integers.

With three arguments, run prints the integers from the first through the second, stepping by the third, e.g.,

run 2 12 2

produces

2
4
6
8
10
12

The argument ordering of start stop step is unlike both jot and seq, which would need

jot - 2 12 2

and

seq 2 2 12

both of which I find difficult to remember.

Undoubtedly, I find start stop step easy to remember because that’s the ordering of arguments in Python’s range() function, which I use all the time. The difference between run and range is that run’s default starting point is 1 instead of 0, and it continues through to the second argument instead of stopping just short of it. In other words, it’s written for humans, not programmers.

As you can see, though, if start, stop, and step don’t align, run won’t generate the stop value. For example,

run 1 10 2

will only print

1
3
5
7
9

because 10 doesn’t fit in the sequence. This was, to me, the best way to interpret arguments that aren’t in sync with one another.

The -f option expects a formatting string in the style of Python’s format method. Again, this syntax is easy for me to remember because I use it frequently. I can, for example, generate a sequential list of parts like this:

run -f 'Part 52Q39-{:02d}' 8 13

gives

Part 52Q39-08
Part 52Q39-09
Part 52Q39-10
Part 52Q39-11
Part 52Q39-12
Part 52Q39-13

The -s option lets me specify the separator between items in the list:

run -s ', ' 5

produces

1, 2, 3, 4, 5

This is like jot’s -s option but unlike seq’s, which treats the argument of -s as a suffix, not a separator:

seq -s ', ' 5

gives

1, 2, 3, 4, 5, 

with the comma and space trailing after the last item, too. I don’t know why anyone would want this.

Like jot, but not seq, run can produce a sequence of characters:

run -c -f 'Apt. {}' A E

gives

Apt. A
Apt. B
Apt. C
Apt. D
Apt. E

The -r option for reversing the list isn’t necessary—I could always pipe the result through sort -r—but I need reversed lists often enough that it’s worth having built-in.

You’ll note that I still use docopt to deal with options and arguments. It’s a great library, so much nicer to use than the supposedly easy argparse. One oddity that came up in this script was that I couldn’t include the default values for -d and -s in the usage string. Normally, I’d do something like

  -s SSS   separator string [default: \n]

and docopt would automatically assign the default string to args['-s'] if run was called without an -s option. But writing it that way caused a linebreak to appear at that point in the help message, which I didn’t want. Even worse, escaping the backslash by writing it as

  -s SSS   separator string [default: \\n]

meant that the literal character pair \n would be the default separator, which was flatly wrong. So I left the defaults out of the usage string and handled them in Lines 23 and 24.

And speaking of escapes, the interpret function in Lines 25–29 is needed to handle escape codes like \n and \t in the format and separator strings. When docopt processes the options, it treats the arguments to -s and -f literally, which is not what I want. When I say

run -s '\t' 5

I want

1   2   3   4   5

with actual tab characters between the numbers, not

1\t2\t3\t4\t5

I learned how to use the codecs module to process escapes from this Stack Overflow page.

I’m not done with run. It doesn’t handle floating point numbers, and its error handling consists of passing Python’s errors along to the user. Since I’m the only user, this latter deficiency isn’t all that bothersome, but I suspect I’ll be wishing I could use it for fractional steps before too long. One of the nice things about homemade programs, though, is that they can grow with you. You get to tweak them as you see how they work—and don’t work—under real-world conditions.