Revert Python 3 changes as they relied on a non-standard module.

This reverts commit b3339ef2f8b1091d932e2936fe825ddde31e129d.
This commit is contained in:
Greg Neagle 2019-05-02 17:21:20 -07:00
parent c7c3ab6c31
commit 1f1615e5a2

View File

@ -23,28 +23,17 @@ A tool to download the parts for an Install macOS app from Apple's
softwareupdate servers and install a functioning Install macOS app onto an
empty disk image'''
# Python 2/3 compatibility shims
from __future__ import (absolute_import,
division,
print_function,
unicode_literals)
from builtins import input
# pylint: disable=wrong-import-order,wrong-import-position
import argparse
import gzip
import os
import plistlib
import subprocess
import sys
try:
from urllib.parse import urlsplit
except ImportError:
from urlparse import urlsplit
import urlparse
import xattr
from xml.dom import minidom
from xml.parsers.expat import ExpatError
# pylint: enable=wrong-import-order,wrong-import-position
DEFAULT_SUCATALOGS = {
@ -67,7 +56,7 @@ def get_seeding_program(sucatalog_url):
'''Returns a seeding program name based on the sucatalog_url'''
try:
seed_catalogs = plistlib.readPlist(SEED_CATALOGS_PLIST)
for key, value in list(seed_catalogs.items()):
for key, value in seed_catalogs.items():
if sucatalog_url == value:
return key
return ''
@ -88,7 +77,7 @@ def get_seeding_programs():
'''Returns the list of seeding program names'''
try:
seed_catalogs = plistlib.readPlist(SEED_CATALOGS_PLIST)
return list(seed_catalogs.keys())
return seed_catalogs.keys()
except (OSError, ExpatError, AttributeError, KeyError):
return ''
@ -105,17 +94,17 @@ def make_sparse_image(volume_name, output_path):
'-volname', volume_name, '-type', 'SPARSE', '-plist', output_path]
try:
output = subprocess.check_output(cmd)
except subprocess.CalledProcessError as err:
print(err, file=sys.stderr)
except subprocess.CalledProcessError, err:
print >> sys.stderr, err
exit(-1)
try:
return plistlib.readPlistFromString(output)[0]
except IndexError as err:
print('Unexpected output from hdiutil: %s' % output, file=sys.stderr)
except IndexError, err:
print >> sys.stderr, 'Unexpected output from hdiutil: %s' % output
exit(-1)
except ExpatError as err:
print('Malformed output from hdiutil: %s' % output, file=sys.stderr)
print(err, file=sys.stderr)
except ExpatError, err:
print >> sys.stderr, 'Malformed output from hdiutil: %s' % output
print >> sys.stderr, err
exit(-1)
@ -123,16 +112,16 @@ def make_compressed_dmg(app_path, diskimagepath):
"""Returns path to newly-created compressed r/o disk image containing
Install macOS.app"""
print(('Making read-only compressed disk image containing %s...'
% os.path.basename(app_path)))
print ('Making read-only compressed disk image containing %s...'
% os.path.basename(app_path))
cmd = ['/usr/bin/hdiutil', 'create', '-fs', 'HFS+',
'-srcfolder', app_path, diskimagepath]
try:
subprocess.check_call(cmd)
except subprocess.CalledProcessError as err:
print(err, file=sys.stderr)
except subprocess.CalledProcessError, err:
print >> sys.stderr, err
else:
print('Disk image created at: %s' % diskimagepath)
print 'Disk image created at: %s' % diskimagepath
def mountdmg(dmgpath):
@ -148,8 +137,7 @@ def mountdmg(dmgpath):
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(pliststr, err) = proc.communicate()
if proc.returncode:
print(
'Error: "%s" while mounting %s.' % (err, dmgname), file=sys.stderr)
print >> sys.stderr, 'Error: "%s" while mounting %s.' % (err, dmgname)
return None
if pliststr:
plist = plistlib.readPlistFromString(pliststr)
@ -169,13 +157,13 @@ def unmountdmg(mountpoint):
stderr=subprocess.PIPE)
(dummy_output, err) = proc.communicate()
if proc.returncode:
print('Polite unmount failed: %s' % err, file=sys.stderr)
print('Attempting to force unmount %s' % mountpoint, file=sys.stderr)
print >> sys.stderr, 'Polite unmount failed: %s' % err
print >> sys.stderr, 'Attempting to force unmount %s' % mountpoint
# try forcing the unmount
retcode = subprocess.call(['/usr/bin/hdiutil', 'detach', mountpoint,
'-force'])
if retcode:
print('Failed to unmount %s' % mountpoint, file=sys.stderr)
print >> sys.stderr, 'Failed to unmount %s' % mountpoint
def install_product(dist_path, target_vol):
@ -185,8 +173,8 @@ def install_product(dist_path, target_vol):
try:
subprocess.check_call(cmd)
return True
except subprocess.CalledProcessError as err:
print(err, file=sys.stderr)
except subprocess.CalledProcessError, err:
print >> sys.stderr, err
return False
@ -203,7 +191,7 @@ def replicate_url(full_url,
'''Downloads a URL and stores it in the same relative path on our
filesystem. Returns a path to the replicated file.'''
path = urlsplit(full_url)[2]
path = urlparse.urlsplit(full_url)[2]
relative_url = path.lstrip('/')
relative_url = os.path.normpath(relative_url)
local_file_path = os.path.join(root_dir, relative_url)
@ -218,10 +206,10 @@ def replicate_url(full_url,
if attempt_resume:
curl_cmd.extend(['-C', '-'])
curl_cmd.append(full_url)
print("Downloading %s..." % full_url)
print "Downloading %s..." % full_url
try:
subprocess.check_call(curl_cmd)
except subprocess.CalledProcessError as err:
except subprocess.CalledProcessError, err:
raise ReplicationError(err)
return local_file_path
@ -234,8 +222,8 @@ def parse_server_metadata(filename):
vers = ''
try:
md_plist = plistlib.readPlist(filename)
except (OSError, IOError, ExpatError) as err:
print('Error reading %s: %s' % (filename, err), file=sys.stderr)
except (OSError, IOError, ExpatError), err:
print >> sys.stderr, 'Error reading %s: %s' % (filename, err)
return {}
vers = md_plist.get('CFBundleShortVersionString', '')
localization = md_plist.get('localization', {})
@ -258,12 +246,12 @@ def get_server_metadata(catalog, product_key, workdir, ignore_cache=False):
smd_path = replicate_url(
url, root_dir=workdir, ignore_cache=ignore_cache)
return smd_path
except ReplicationError as err:
print((
'Could not replicate %s: %s' % (url, err)), file=sys.stderr)
except ReplicationError, err:
print >> sys.stderr, (
'Could not replicate %s: %s' % (url, err))
return None
except KeyError:
print('Malformed catalog.', file=sys.stderr)
print >> sys.stderr, 'Malformed catalog.'
return None
@ -274,10 +262,10 @@ def parse_dist(filename):
try:
dom = minidom.parse(filename)
except ExpatError:
print('Invalid XML in %s' % filename, file=sys.stderr)
print >> sys.stderr, 'Invalid XML in %s' % filename
return dist_info
except IOError as err:
print('Error reading %s: %s' % (filename, err), file=sys.stderr)
except IOError, err:
print >> sys.stderr, 'Error reading %s: %s' % (filename, err)
return dist_info
auxinfos = dom.getElementsByTagName('auxinfo')
@ -311,8 +299,8 @@ def download_and_parse_sucatalog(sucatalog, workdir, ignore_cache=False):
try:
localcatalogpath = replicate_url(
sucatalog, root_dir=workdir, ignore_cache=ignore_cache)
except ReplicationError as err:
print('Could not replicate %s: %s' % (sucatalog, err), file=sys.stderr)
except ReplicationError, err:
print >> sys.stderr, 'Could not replicate %s: %s' % (sucatalog, err)
exit(-1)
if os.path.splitext(localcatalogpath)[1] == '.gz':
with gzip.open(localcatalogpath) as the_file:
@ -320,19 +308,17 @@ def download_and_parse_sucatalog(sucatalog, workdir, ignore_cache=False):
try:
catalog = plistlib.readPlistFromString(content)
return catalog
except ExpatError as err:
print(
'Error reading %s: %s' % (localcatalogpath, err),
file=sys.stderr)
except ExpatError, err:
print >> sys.stderr, (
'Error reading %s: %s' % (localcatalogpath, err))
exit(-1)
else:
try:
catalog = plistlib.readPlist(localcatalogpath)
return catalog
except (OSError, IOError, ExpatError) as err:
print(
'Error reading %s: %s' % (localcatalogpath, err),
file=sys.stderr)
except (OSError, IOError, ExpatError), err:
print >> sys.stderr, (
'Error reading %s: %s' % (localcatalogpath, err))
exit(-1)
@ -369,9 +355,8 @@ def os_installer_product_info(catalog, workdir, ignore_cache=False):
try:
dist_path = replicate_url(
dist_url, root_dir=workdir, ignore_cache=ignore_cache)
except ReplicationError as err:
print(
'Could not replicate %s: %s' % (dist_url, err), file=sys.stderr)
except ReplicationError, err:
print >> sys.stderr, 'Could not replicate %s: %s' % (dist_url, err)
dist_info = parse_dist(dist_path)
product_info[product_key]['DistributionPath'] = dist_path
product_info[product_key].update(dist_info)
@ -392,19 +377,18 @@ def replicate_product(catalog, product_id, workdir, ignore_cache=False):
package['URL'], root_dir=workdir,
show_progress=True, ignore_cache=ignore_cache,
attempt_resume=(not ignore_cache))
except ReplicationError as err:
print(
'Could not replicate %s: %s' % (package['URL'], err),
file=sys.stderr)
except ReplicationError, err:
print >> sys.stderr, (
'Could not replicate %s: %s' % (package['URL'], err))
exit(-1)
if 'MetadataURL' in package:
try:
replicate_url(package['MetadataURL'], root_dir=workdir,
ignore_cache=ignore_cache)
except ReplicationError as err:
print((
except ReplicationError, err:
print >> sys.stderr, (
'Could not replicate %s: %s'
% (package['MetadataURL'], err)), file=sys.stderr)
% (package['MetadataURL'], err))
exit(-1)
@ -453,19 +437,18 @@ def main():
elif args.seedprogram:
su_catalog_url = get_seed_catalog(args.seedprogram)
if not su_catalog_url:
print((
print >> sys.stderr, (
'Could not find a catalog url for seed program %s'
% args.seedprogram), file=sys.stderr)
print((
% args.seedprogram)
print >> sys.stderr, (
'Valid seeding programs are: %s'
% ', '.join(get_seeding_programs())), file=sys.stderr)
% ', '.join(get_seeding_programs()))
exit(-1)
else:
su_catalog_url = get_default_catalog()
if not su_catalog_url:
print(
'Could not find a default catalog url for this OS version.',
file=sys.stderr)
print >> sys.stderr, (
'Could not find a default catalog url for this OS version.')
exit(-1)
# download sucatalog and look for products that are for macOS installers
@ -475,33 +458,32 @@ def main():
catalog, args.workdir, ignore_cache=args.ignore_cache)
if not product_info:
print(
'No macOS installer products found in the sucatalog.',
file=sys.stderr)
print >> sys.stderr, (
'No macOS installer products found in the sucatalog.')
exit(-1)
# display a menu of choices (some seed catalogs have multiple installers)
print('%2s %12s %10s %8s %11s %s' % ('#', 'ProductID', 'Version',
'Build', 'Post Date', 'Title'))
print '%2s %12s %10s %8s %11s %s' % ('#', 'ProductID', 'Version',
'Build', 'Post Date', 'Title')
for index, product_id in enumerate(product_info):
print('%2s %12s %10s %8s %11s %s' % (
print '%2s %12s %10s %8s %11s %s' % (
index + 1,
product_id,
product_info[product_id]['version'],
product_info[product_id]['BUILD'],
product_info[product_id]['PostDate'].strftime('%Y-%m-%d'),
product_info[product_id]['title']
))
)
answer = input(
answer = raw_input(
'\nChoose a product to download (1-%s): ' % len(product_info))
try:
index = int(answer) - 1
if index < 0:
raise ValueError
product_id = list(product_info.keys())[index]
product_id = product_info.keys()[index]
except (ValueError, IndexError):
print('Exiting.')
print 'Exiting.'
exit(0)
# download all the packages for the selected product
@ -517,7 +499,7 @@ def main():
os.unlink(sparse_diskimage_path)
# make an empty sparseimage and mount it
print('Making empty sparseimage...')
print 'Making empty sparseimage...'
sparse_diskimage_path = make_sparse_image(volname, sparse_diskimage_path)
mountpoint = mountdmg(sparse_diskimage_path)
if mountpoint:
@ -526,7 +508,7 @@ def main():
product_info[product_id]['DistributionPath'],
mountpoint)
if not success:
print('Product installation failed.', file=sys.stderr)
print >> sys.stderr, 'Product installation failed.'
unmountdmg(mountpoint)
exit(-1)
# add the seeding program xattr to the app if applicable
@ -535,7 +517,7 @@ def main():
installer_app = find_installer_app(mountpoint)
if installer_app:
xattr.setxattr(installer_app, 'SeedProgram', seeding_program)
print('Product downloaded and installed to %s' % sparse_diskimage_path)
print 'Product downloaded and installed to %s' % sparse_diskimage_path
if args.raw:
unmountdmg(mountpoint)
else: