swc-windows-installer.py: Install nano source syntax highlighters
authorW. Trevor King <wking@tremily.us>
Wed, 5 Mar 2014 17:40:42 +0000 (09:40 -0800)
committerW. Trevor King <wking@tremily.us>
Wed, 5 Mar 2014 17:45:45 +0000 (09:45 -0800)
Make writing Python source code (and a number of other languages) a
bit more exciting for the eyes and easier on the brain by coloring
strings, comments, and such.

We need to install the source tarball because the compiled zip doesn't
include the syntax highlighting scripts.  To make that easier, I've
extracted out the download / hash-check functionality into a new
download(), which I use in both the old zip_install() and the new
tar_install().  If the user doesn't have an existing ~/.nanorc, we
populate it by adding 'include' options for each of the syntax files
in the Nano tarball.  If they do have an existing ~/.nanorc, I assume
they know what they're doing ;).

The default '*' compression is for transparent detection [1], but you
can override it to explicitly select the compression type if you like.

There's a bit of ugliness to work around the lack of a
--strip-components analog in TarFile.extractall [2].  Instead, we
iterate over the TarInfo members and strip leading components from
their 'name' by hand [3].

[1]: http://docs.python.org/3/library/tarfile.html#tarfile.open
[2]: http://docs.python.org/3/library/tarfile.html#tarfile.TarFile.extractall
[3]: http://docs.python.org/3/library/tarfile.html#tarfile.TarInfo.name

swc-windows-installer.py

index f10377deb6fc652e6ef9ddfb464872bde3e8c7e2..3c9d9f9e0074be13f6e5d6b2cd3bbe5211952870 100755 (executable)
@@ -6,6 +6,7 @@ Helps mimic a *nix environment on Windows with as little work as possible.
 
 The script:
 * Installs nano and makes it accessible from msysgit
+* Creates ~/.nanorc with links to syntax highlighting configs
 * Provides standard nosetests behavior for msysgit
 
 To use:
@@ -27,6 +28,7 @@ except ImportError:  # Python 2
     from StringIO import StringIO as _BytesIO
 import os
 import re
+import tarfile
 try:  # Python 3
     from urllib.request import urlopen as _urlopen
 except ImportError:  # Python 2
@@ -34,18 +36,74 @@ except ImportError:  # Python 2
 import zipfile
 
 
-def zip_install(url, sha1, install_directory):
-    """Download and install a zipped bundle of compiled software"""
+def download(url, sha1):
+    """Download a file and verify it's hash"""
     r = _urlopen(url)
-    zip_bytes = r.read()
-    download_sha1 = hashlib.sha1(zip_bytes).hexdigest()
+    byte_content = r.read()
+    download_sha1 = hashlib.sha1(byte_content).hexdigest()
     if download_sha1 != sha1:
         raise ValueError(
             'downloaded {!r} has the wrong SHA1 hash: {} != {}'.format(
-                url, downloaded_sha1, sha1))
-    zip_io = _BytesIO(zip_bytes)
-    zip_file = zipfile.ZipFile(zip_io)
+                url, download_sha1, sha1))
+    return byte_content
+
+
+def splitall(path):
+    """Split a path into a list of components
+
+    >>> splitall('nano-2.2.6/doc/Makefile.am')
+    ['nano-2.2.6', 'doc', 'Makefile.am']
+    """
+    parts = []
+    while True:
+        head, tail = os.path.split(path)
+        if tail:
+            parts.insert(0, tail)
+        elif head:
+            parts.insert(0, head)
+            break
+        else:
+            break
+        path = head
+    return parts
+
+
+def transform(tarinfo, strip_components=0):
+    """Transform TarInfo objects for extraction"""
+    path_components = splitall(tarinfo.name)
+    try:
+        tarinfo.name = os.path.join(*path_components[strip_components:])
+    except TypeError:
+        if len(path_components) <= strip_components:
+            return None
+        raise
+    return tarinfo
+
+
+def tar_install(url, sha1, install_directory, compression='*',
+                strip_components=0):
+    """Download and install a tar bundle"""
+    if not os.path.isdir(install_directory):
+        tar_bytes = download(url=url, sha1=sha1)
+        tar_io = _BytesIO(tar_bytes)
+        filename = os.path.basename(url)
+        mode = 'r:{}'.format(compression)
+        tar_file = tarfile.open(filename, mode, tar_io)
+        os.makedirs(install_directory)
+        members = [
+            transform(tarinfo=tarinfo, strip_components=strip_components)
+            for tarinfo in tar_file]
+        tar_file.extractall(
+            path=install_directory,
+            members=[m for m in members if m is not None])
+
+
+def zip_install(url, sha1, install_directory):
+    """Download and install a zipped bundle"""
     if not os.path.isdir(install_directory):
+        zip_bytes = download(url=url, sha1=sha1)
+        zip_io = _BytesIO(zip_bytes)
+        zip_file = zipfile.ZipFile(zip_io)
         os.makedirs(install_directory)
         zip_file.extractall(install_directory)
 
@@ -58,6 +116,23 @@ def install_nano(install_directory):
         install_directory=install_directory)
 
 
+def install_nanorc(install_directory):
+    """Download and install nano syntax highlighting"""
+    tar_install(
+        url='http://www.nano-editor.org/dist/v2.2/nano-2.2.6.tar.gz',
+        sha1='f2a628394f8dda1b9f28c7e7b89ccb9a6dbd302a',
+        install_directory=install_directory,
+        strip_components=1)
+    nanorc = os.path.join(os.path.expanduser('~'), '.nanorc')
+    if not os.path.isfile(nanorc):
+        syntax_dir = os.path.join(install_directory, 'doc', 'syntax')
+        with open(nanorc, 'w') as f:
+            for filename in os.listdir(syntax_dir):
+                if filename.endswith('.nanorc'):
+                    path = os.path.join(syntax_dir, filename)
+                    f.write('include {}\n'.format(path))
+
+
 def create_nosetests_entry_point(python_scripts_directory):
     """Creates a terminal-based nosetests entry point for msysgit"""
     contents = '\n'.join([
@@ -108,9 +183,11 @@ def make_posix_path(windows_path):
 def main():
     swc_dir = os.path.join(os.path.expanduser('~'), '.swc')
     bin_dir = os.path.join(swc_dir, 'bin')
-    create_nosetests_entry_point(python_scripts_directory=bin_dir)
     nano_dir = os.path.join(swc_dir, 'lib', 'nano')
+    nanorc_dir = os.path.join(swc_dir, 'share', 'nanorc')
+    create_nosetests_entry_point(python_scripts_directory=bin_dir)
     install_nano(install_directory=nano_dir)
+    install_nanorc(install_directory=nanorc_dir)
     update_bash_profile(extra_paths=(nano_dir, bin_dir))