Python and platforms

After my last post on the scripts that generate my Apple sales graphs (which I’ll need again when the new quarterly results are posted tomorrow), I was shamed by Rosemary Orchard and Nathan Grigg into rewriting them to run on both macOS and iOS (the latter via Pythonista). It turned out to be pretty easy, with only a couple of additions needed.

I won’t bore you with the entire script, I’ll just bore you with the new stuff that lets the script run on both platforms. There were two changes.

First, since there is no OptiPNG for iOS, this line in the original script,

python:
subprocess.run(['optipng', pngFile])

which processed the just-saved file on my Mac, had to go. The fix, though, wasn’t too much different: just run OptiPNG on the server after the file was uploaded. I already had an SSH connection to the server, so all I had to do was add this line,

python:
sshIn, sshOut, sshErr = ssh.exec_command("optipng '{}'".format(pngDest))

before closing the connection. The exec_command function is part of the paramiko SSH library, and pngDest is a variable that contains the full path on the server to the newly uploaded PNG file.

The second change was really more of an addition than a change. I decided I wanted the script to put the uploaded PNG’s URL on the clipboard to make it easy to paste into a blog post. This is easy to do on the Mac, where you just use subprocess to run the pbcopy command;1 and it’s even easier to do on iOS, where Pythonista includes the handy clipboard library. The problem comes in figuring out which device the script is running on.

(It’s possible this problem has already been solved and is sitting in GitHub repository, but I didn’t find it. Al Sweigart’s pyperclip is a cross-platform library for moving text to and from the clipboard, but the platforms it crosses are the traditional Windows, Mac, and Linux—no iOS.)

At first, I thought calling os.name would give me what I needed, but it returns posix on both macOS and iOS. After a bit of searching, I hit upon the platform library, which I’d never used before. In particular, the platform.platform() function returns the string

Darwin-17.6.0-x86_64-i386-64bit

on my 2012 iMac at home,

Darwin-17.7.0-x86_64-i386-64bit

on my 2017 iMac at work,

Darwin-17.7.0-iPad6,3-64bit

on my iPad Pro, and

Darwin-17.7.0-iPhone8,1-63bit

on my iPhone 6s. I don’t understand why the minor Darwin number is one less on the home iMac than it is everywhere else, but it doesn’t matter for what I need here. The key is that Macs have “x86” in the string (at least for now) and it’s a safe bet that iOS devices never will.

Thus, the following section of code at the end of my script:

python:
if 'x86' in platform.platform():
  import subprocess
  subprocess.Popen('pbcopy', stdin=subprocess.PIPE).communicate(pngURL.encode())
else:
  import clipboard
  clipboard.set(pngURL)

As its name implies, pngURL is a string that contains the URL of the just-uploaded and optimized PNG file. Applying encode to it before sending it to communicate is necessary because communicate takes only bytes.

Oh, there is one more thing. I used to keep the Apple sales scripts and data files in a Dropbox folder, but now I have them in a folder on iCloud Drive. Pythonista’s external files feature can link to individual Dropbox files, but I couldn’t get it to link to a folder, and I kept getting errors whenever a script on Dropbox tried to save a file. Both of these problems disappeared when I moved everything to iCloud.2

So thanks to Rosemary and Nathan for the nudge. I didn’t think I wanted an iOS-resident solution, but now that I have it, it seems pretty neat.


  1. That link should go to Apple’s online man page for the pbcopy command, but Apple has decided either to delete its man pages or move them where neither I nor Google can find them. Have I complained about this before? Yes, and I’m complaining about it again. 

  2. I’ve been thinking about switching entirely from Dropbox to iCloud, but that’s a topic for another post.