class Profile(object): """Handles all operations regarding profile. Created new profiles, installs extensions, sets preferences and handles cleanup.""" def __init__(self, profile=None, addons=None, addon_manifests=None, apps=None, preferences=None, locations=None, proxy=None, restore=True): """ :param profile: Path to the profile :param addons: String of one or list of addons to install :param addon_manifests: Manifest for addons, see http://ahal.ca/blog/2011/bulk-installing-fx-addons/ :param apps: Dictionary or class of webapps to install :param preferences: Dictionary or class of preferences :param locations: locations to proxy :param proxy: setup a proxy - dict of server-loc,server-port,ssl-port :param restore: If true remove all installed addons preferences when cleaning up """ # if true, remove installed addons/prefs afterwards self.restore = restore # prefs files written to self.written_prefs = set() # our magic markers nonce = '%s %s' % (str(time.time()), uuid.uuid4()) self.delimeters = ('#MozRunner Prefs Start %s' % nonce, '#MozRunner Prefs End %s' % nonce) # Handle profile creation self.create_new = not profile if profile: # Ensure we have a full path to the profile self.profile = os.path.abspath(os.path.expanduser(profile)) if not os.path.exists(self.profile): os.makedirs(self.profile) else: self.profile = self.create_new_profile() # set preferences if hasattr(self.__class__, 'preferences'): # class preferences self.set_preferences(self.__class__.preferences) self._preferences = preferences if preferences: # supplied preferences if isinstance(preferences, dict): # unordered preferences = preferences.items() # sanity check assert not [i for i in preferences if len(i) != 2] else: preferences = [] self.set_preferences(preferences) # set permissions self._locations = locations # store this for reconstruction self._proxy = proxy self.permissions = Permissions(self.profile, locations) prefs_js, user_js = self.permissions.network_prefs(proxy) self.set_preferences(prefs_js, 'prefs.js') self.set_preferences(user_js) # handle addon installation self.addon_manager = AddonManager(self.profile) self.addon_manager.install_addons(addons, addon_manifests) # handle webapps self.webapps = WebappCollection(profile=self.profile, apps=apps) self.webapps.update_manifests() def exists(self): """returns whether the profile exists or not""" return os.path.exists(self.profile) def reset(self): """ reset the profile to the beginning state """ self.cleanup() if self.create_new: profile = None else: profile = self.profile self.__init__(profile=profile, addons=self.addon_manager.installed_addons, addon_manifests=self.addon_manager.installed_manifests, preferences=self._preferences, locations=self._locations, proxy=self._proxy) @classmethod def clone(cls, path_from, path_to=None, **kwargs): """Instantiate a temporary profile via cloning - path: path of the basis to clone - kwargs: arguments to the profile constructor """ if not path_to: tempdir = tempfile.mkdtemp() # need an unused temp dir name rmtree(tempdir) # copytree requires that dest does not exist path_to = tempdir copytree(path_from, path_to) def cleanup_clone(fn): """Deletes a cloned profile when restore is True""" def wrapped(self): fn(self) if self.restore and os.path.exists(self.profile): rmtree(self.profile, onerror=self._cleanup_error) return wrapped c = cls(path_to, **kwargs) c.__del__ = c.cleanup = types.MethodType(cleanup_clone(cls.cleanup), c) return c def create_new_profile(self): """Create a new clean temporary profile which is a simple empty folder""" return tempfile.mkdtemp(suffix='.mozrunner') ### methods for preferences def set_preferences(self, preferences, filename='user.js'): """Adds preferences dict to profile preferences""" # append to the file prefs_file = os.path.join(self.profile, filename) f = open(prefs_file, 'a') if preferences: # note what files we've touched self.written_prefs.add(filename) # opening delimeter f.write('\n%s\n' % self.delimeters[0]) # write the preferences Preferences.write(f, preferences) # closing delimeter f.write('%s\n' % self.delimeters[1]) f.close() def pop_preferences(self, filename): """ pop the last set of preferences added returns True if popped """ lines = file(os.path.join(self.profile, filename)).read().splitlines() def last_index(_list, value): """ returns the last index of an item; this should actually be part of python code but it isn't """ for index in reversed(range(len(_list))): if _list[index] == value: return index s = last_index(lines, self.delimeters[0]) e = last_index(lines, self.delimeters[1]) # ensure both markers are found if s is None: assert e is None, '%s found without %s' % (self.delimeters[1], self.delimeters[0]) return False # no preferences found elif e is None: assert s is None, '%s found without %s' % (self.delimeters[0], self.delimeters[1]) # ensure the markers are in the proper order assert e > s, '%s found at %s, while %s found at %s' % ( self.delimeters[1], e, self.delimeters[0], s) # write the prefs cleaned_prefs = '\n'.join(lines[:s] + lines[e + 1:]) f = file(os.path.join(self.profile, 'user.js'), 'w') f.write(cleaned_prefs) f.close() return True def clean_preferences(self): """Removed preferences added by mozrunner.""" for filename in self.written_prefs: if not os.path.exists(os.path.join(self.profile, filename)): # file has been deleted break while True: if not self.pop_preferences(filename): break ### cleanup def _cleanup_error(self, function, path, excinfo): """ Specifically for windows we need to handle the case where the windows process has not yet relinquished handles on files, so we do a wait/try construct and timeout if we can't get a clear road to deletion """ try: from exceptions import WindowsError from time import sleep def is_file_locked(): return excinfo[0] is WindowsError and excinfo[1].winerror == 32 if excinfo[0] is WindowsError and excinfo[1].winerror == 32: # Then we're on windows, wait to see if the file gets unlocked # we wait 10s count = 0 while count < 10: sleep(1) try: function(path) break except: count += 1 except ImportError: # We can't re-raise an error, so we'll hope the stuff above us will throw pass def cleanup(self): """Cleanup operations for the profile.""" if self.restore: if self.create_new: if os.path.exists(self.profile): rmtree(self.profile, onerror=self._cleanup_error) else: self.clean_preferences() self.addon_manager.clean_addons() self.permissions.clean_db() self.webapps.clean() __del__ = cleanup
class Profile(object): """Handles all operations regarding profile. Created new profiles, installs extensions, sets preferences and handles cleanup.""" def __init__(self, profile=None, addons=None, addon_manifests=None, preferences=None, locations=None, proxy=None, restore=True): """ :param profile: Path to the profile :param addons: String of one or list of addons to install :param addon_manifests: Manifest for addons, see http://ahal.ca/blog/2011/bulk-installing-fx-addons/ :param preferences: Dictionary or class of preferences :param locations: locations to proxy :param proxy: setup a proxy - dict of server-loc,server-port,ssl-port :param restore: If true remove all installed addons preferences when cleaning up """ # if true, remove installed addons/prefs afterwards self.restore = restore # prefs files written to self.written_prefs = set() # our magic markers nonce = '%s %s' % (str(time.time()), uuid.uuid4()) self.delimeters = ('#MozRunner Prefs Start %s' % nonce,'#MozRunner Prefs End %s' % nonce) # Handle profile creation self.create_new = not profile if profile: # Ensure we have a full path to the profile self.profile = os.path.abspath(os.path.expanduser(profile)) if not os.path.exists(self.profile): os.makedirs(self.profile) else: self.profile = self.create_new_profile() # set preferences if hasattr(self.__class__, 'preferences'): # class preferences self.set_preferences(self.__class__.preferences) self._preferences = preferences if preferences: # supplied preferences if isinstance(preferences, dict): # unordered preferences = preferences.items() # sanity check assert not [i for i in preferences if len(i) != 2] else: preferences = [] self.set_preferences(preferences) # set permissions self._locations = locations # store this for reconstruction self._proxy = proxy self.permissions = Permissions(self.profile, locations) prefs_js, user_js = self.permissions.network_prefs(proxy) self.set_preferences(prefs_js, 'prefs.js') self.set_preferences(user_js) # handle addon installation self.addon_manager = AddonManager(self.profile) self.addon_manager.install_addons(addons, addon_manifests) def exists(self): """returns whether the profile exists or not""" return os.path.exists(self.profile) def reset(self): """ reset the profile to the beginning state """ self.cleanup() if self.create_new: profile = None else: profile = self.profile self.__init__(profile=profile, addons=self.addon_manager.installed_addons, addon_manifests=self.addon_manager.installed_manifests, preferences=self._preferences, locations=self._locations, proxy = self._proxy) def create_new_profile(self): """Create a new clean profile in tmp which is a simple empty folder""" profile = tempfile.mkdtemp(suffix='.mozrunner') return profile ### methods for preferences def set_preferences(self, preferences, filename='user.js'): """Adds preferences dict to profile preferences""" # append to the file prefs_file = os.path.join(self.profile, filename) f = open(prefs_file, 'a') if preferences: # note what files we've touched self.written_prefs.add(filename) if isinstance(preferences, dict): # order doesn't matter preferences = preferences.items() # write the preferences f.write('\n%s\n' % self.delimeters[0]) _prefs = [(json.dumps(k), json.dumps(v) ) for k, v in preferences] for _pref in _prefs: f.write('user_pref(%s, %s);\n' % _pref) f.write('%s\n' % self.delimeters[1]) f.close() def pop_preferences(self, filename): """ pop the last set of preferences added returns True if popped """ lines = file(os.path.join(self.profile, filename)).read().splitlines() def last_index(_list, value): """ returns the last index of an item; this should actually be part of python code but it isn't """ for index in reversed(range(len(_list))): if _list[index] == value: return index s = last_index(lines, self.delimeters[0]) e = last_index(lines, self.delimeters[1]) # ensure both markers are found if s is None: assert e is None, '%s found without %s' % (self.delimeters[1], self.delimeters[0]) return False # no preferences found elif e is None: assert s is None, '%s found without %s' % (self.delimeters[0], self.delimeters[1]) # ensure the markers are in the proper order assert e > s, '%s found at %s, while %s found at %s' % (self.delimeters[1], e, self.delimeters[0], s) # write the prefs cleaned_prefs = '\n'.join(lines[:s] + lines[e+1:]) f = file(os.path.join(self.profile, 'user.js'), 'w') f.write(cleaned_prefs) f.close() return True def clean_preferences(self): """Removed preferences added by mozrunner.""" for filename in self.written_prefs: if not os.path.exists(os.path.join(self.profile, filename)): # file has been deleted break while True: if not self.pop_preferences(filename): break ### cleanup def _cleanup_error(self, function, path, excinfo): """ Specifically for windows we need to handle the case where the windows process has not yet relinquished handles on files, so we do a wait/try construct and timeout if we can't get a clear road to deletion """ try: from exceptions import WindowsError from time import sleep def is_file_locked(): return excinfo[0] is WindowsError and excinfo[1].winerror == 32 if excinfo[0] is WindowsError and excinfo[1].winerror == 32: # Then we're on windows, wait to see if the file gets unlocked # we wait 10s count = 0 while count < 10: sleep(1) try: function(path) break except: count += 1 except ImportError: # We can't re-raise an error, so we'll hope the stuff above us will throw pass def cleanup(self): """Cleanup operations for the profile.""" if self.restore: if self.create_new: if os.path.exists(self.profile): rmtree(self.profile, onerror=self._cleanup_error) else: self.clean_preferences() self.addon_manager.clean_addons() self.permissions.clean_db() __del__ = cleanup
class Profile(object): """Handles all operations regarding profile. Created new profiles, installs extensions, sets preferences and handles cleanup. :param profile: Path to the profile :param addons: String of one or list of addons to install :param addon_manifests: Manifest for addons, see http://ahal.ca/blog/2011/bulk-installing-fx-addons/ :param apps: Dictionary or class of webapps to install :param preferences: Dictionary or class of preferences :param locations: ServerLocations object :param proxy: setup a proxy :param restore: If true remove all added addons and preferences when cleaning up """ def __init__(self, profile=None, addons=None, addon_manifests=None, apps=None, preferences=None, locations=None, proxy=None, restore=True): # if true, remove installed addons/prefs afterwards self.restore = restore # prefs files written to self.written_prefs = set() # our magic markers nonce = '%s %s' % (str(time.time()), uuid.uuid4()) self.delimeters = ('#MozRunner Prefs Start %s' % nonce,'#MozRunner Prefs End %s' % nonce) # Handle profile creation self.create_new = not profile if profile: # Ensure we have a full path to the profile self.profile = os.path.abspath(os.path.expanduser(profile)) if not os.path.exists(self.profile): os.makedirs(self.profile) else: self.profile = self.create_new_profile() # set preferences if hasattr(self.__class__, 'preferences'): # class preferences self.set_preferences(self.__class__.preferences) self._preferences = preferences if preferences: # supplied preferences if isinstance(preferences, dict): # unordered preferences = preferences.items() # sanity check assert not [i for i in preferences if len(i) != 2] else: preferences = [] self.set_preferences(preferences) # set permissions self._locations = locations # store this for reconstruction self._proxy = proxy self.permissions = Permissions(self.profile, locations) prefs_js, user_js = self.permissions.network_prefs(proxy) self.set_preferences(prefs_js, 'prefs.js') self.set_preferences(user_js) # handle addon installation self.addon_manager = AddonManager(self.profile, restore=self.restore) self.addon_manager.install_addons(addons, addon_manifests) # handle webapps self.webapps = WebappCollection(profile=self.profile, apps=apps) self.webapps.update_manifests() def exists(self): """returns whether the profile exists or not""" return os.path.exists(self.profile) def reset(self): """ reset the profile to the beginning state """ self.cleanup() if self.create_new: profile = None else: profile = self.profile self.__init__(profile=profile, addons=self.addon_manager.installed_addons, addon_manifests=self.addon_manager.installed_manifests, preferences=self._preferences, locations=self._locations, proxy = self._proxy) @classmethod def clone(cls, path_from, path_to=None, **kwargs): """Instantiate a temporary profile via cloning - path: path of the basis to clone - kwargs: arguments to the profile constructor """ if not path_to: tempdir = tempfile.mkdtemp() # need an unused temp dir name rmtree(tempdir) # copytree requires that dest does not exist path_to = tempdir copytree(path_from, path_to) def cleanup_clone(fn): """Deletes a cloned profile when restore is True""" def wrapped(self): fn(self) if self.restore and os.path.exists(self.profile): rmtree(self.profile, onerror=self._cleanup_error) return wrapped c = cls(path_to, **kwargs) c.__del__ = c.cleanup = types.MethodType(cleanup_clone(cls.cleanup), c) return c def create_new_profile(self): """Create a new clean temporary profile which is a simple empty folder""" return tempfile.mkdtemp(suffix='.mozrunner') ### methods for preferences def set_preferences(self, preferences, filename='user.js'): """Adds preferences dict to profile preferences""" # append to the file prefs_file = os.path.join(self.profile, filename) f = open(prefs_file, 'a') if preferences: # note what files we've touched self.written_prefs.add(filename) # opening delimeter f.write('\n%s\n' % self.delimeters[0]) # write the preferences Preferences.write(f, preferences) # closing delimeter f.write('%s\n' % self.delimeters[1]) f.close() def pop_preferences(self, filename): """ pop the last set of preferences added returns True if popped """ path = os.path.join(self.profile, filename) with file(path) as f: lines = f.read().splitlines() def last_index(_list, value): """ returns the last index of an item; this should actually be part of python code but it isn't """ for index in reversed(range(len(_list))): if _list[index] == value: return index s = last_index(lines, self.delimeters[0]) e = last_index(lines, self.delimeters[1]) # ensure both markers are found if s is None: assert e is None, '%s found without %s' % (self.delimeters[1], self.delimeters[0]) return False # no preferences found elif e is None: assert s is None, '%s found without %s' % (self.delimeters[0], self.delimeters[1]) # ensure the markers are in the proper order assert e > s, '%s found at %s, while %s found at %s' % (self.delimeters[1], e, self.delimeters[0], s) # write the prefs cleaned_prefs = '\n'.join(lines[:s] + lines[e+1:]) with file(path, 'w') as f: f.write(cleaned_prefs) return True def clean_preferences(self): """Removed preferences added by mozrunner.""" for filename in self.written_prefs: if not os.path.exists(os.path.join(self.profile, filename)): # file has been deleted break while True: if not self.pop_preferences(filename): break ### cleanup def _cleanup_error(self, function, path, excinfo): """ Specifically for windows we need to handle the case where the windows process has not yet relinquished handles on files, so we do a wait/try construct and timeout if we can't get a clear road to deletion """ try: from exceptions import WindowsError from time import sleep def is_file_locked(): return excinfo[0] is WindowsError and excinfo[1].winerror == 32 if excinfo[0] is WindowsError and excinfo[1].winerror == 32: # Then we're on windows, wait to see if the file gets unlocked # we wait 10s count = 0 while count < 10: sleep(1) try: function(path) break except: count += 1 except ImportError: # We can't re-raise an error, so we'll hope the stuff above us will throw pass def cleanup(self): """Cleanup operations for the profile.""" if self.restore: if self.create_new: if os.path.exists(self.profile): rmtree(self.profile, onerror=self._cleanup_error) else: self.clean_preferences() self.addon_manager.clean_addons() self.permissions.clean_db() self.webapps.clean() __del__ = cleanup ### methods for introspection def summary(self, return_parts=False): """ returns string summarizing profile information. if return_parts is true, return the (Part_name, value) list of tuples instead of the assembled string """ parts = [('Path', self.profile)] # profile path # directory tree parts.append(('Files', '\n%s' % tree(self.profile))) # preferences for prefs_file in ('user.js', 'prefs.js'): path = os.path.join(self.profile, prefs_file) if os.path.exists(path): # prefs that get their own section # This is currently only 'network.proxy.autoconfig_url' # but could be expanded to include others section_prefs = ['network.proxy.autoconfig_url'] line_length = 80 line_length_buffer = 10 # buffer for 80 character display: length = 80 - len(key) - len(': ') - line_length_buffer line_length_buffer += len(': ') def format_value(key, value): if key not in section_prefs: return value max_length = line_length - len(key) - line_length_buffer if len(value) > max_length: value = '%s...' % value[:max_length] return value prefs = Preferences.read_prefs(path) if prefs: prefs = dict(prefs) parts.append((prefs_file, '\n%s' %('\n'.join(['%s: %s' % (key, format_value(key, prefs[key])) for key in sorted(prefs.keys()) ])))) # Currently hardcorded to 'network.proxy.autoconfig_url' # but could be generalized, possibly with a generalized (simple) # JS-parser network_proxy_autoconfig = prefs.get('network.proxy.autoconfig_url') if network_proxy_autoconfig and network_proxy_autoconfig.strip(): network_proxy_autoconfig = network_proxy_autoconfig.strip() lines = network_proxy_autoconfig.replace(';', ';\n').splitlines() lines = [line.strip() for line in lines] origins_string = 'var origins = [' origins_end = '];' if origins_string in lines[0]: start = lines[0].find(origins_string) end = lines[0].find(origins_end, start); splitline = [lines[0][:start], lines[0][start:start+len(origins_string)-1], ] splitline.extend(lines[0][start+len(origins_string):end].replace(',', ',\n').splitlines()) splitline.append(lines[0][end:]) lines[0:1] = [i.strip() for i in splitline] parts.append(('Network Proxy Autoconfig, %s' % (prefs_file), '\n%s' % '\n'.join(lines))) if return_parts: return parts retval = '%s\n' % ('\n\n'.join(['[%s]: %s' % (key, value) for key, value in parts])) return retval __str__ = summary
class Profile(object): """Handles all operations regarding profile. Created new profiles, installs extensions, sets preferences and handles cleanup.""" def __init__( self, profile=None, # Path to the profile addons=None, # String of one or list of addons to install addon_manifests=None, # Manifest for addons, see http://ahal.ca/blog/2011/bulk-installing-fx-addons/ preferences=None, # Dictionary or class of preferences locations=None, # locations to proxy proxy=False, # setup a proxy restore=True # If true remove all installed addons preferences when cleaning up ): # if true, remove installed addons/prefs afterwards self.restore = restore # prefs files written to self.written_prefs = set() # our magic markers self.delimeters = ('#MozRunner Prefs Start', '#MozRunner Prefs End') # Handle profile creation self.create_new = not profile if profile: # Ensure we have a full path to the profile self.profile = os.path.abspath(os.path.expanduser(profile)) if not os.path.exists(self.profile): os.makedirs(self.profile) else: self.profile = self.create_new_profile() # set preferences if hasattr(self.__class__, 'preferences'): # class preferences self.set_preferences(self.__class__.preferences) self._preferences = preferences if preferences: # supplied preferences if isinstance(preferences, dict): # unordered preferences = preferences.items() # sanity check assert not [i for i in preferences if len(i) != 2] else: preferences = [] self.set_preferences(preferences) # set permissions self._locations = locations # store this for reconstruction self._proxy = proxy self.permissions = Permissions(self.profile, locations) prefs_js, user_js = self.permissions.network_prefs(proxy) self.set_preferences(prefs_js, 'prefs.js') self.set_preferences(user_js) # handle addon installation self.addon_manager = AddonManager(self.profile) self.addon_manager.install_addons(addons, addon_manifests) def exists(self): """returns whether the profile exists or not""" return os.path.exists(self.profile) def reset(self): """ reset the profile to the beginning state """ self.cleanup() if self.create_new: profile = None else: profile = self.profile self.__init__(profile=profile, addons=self.addon_manager.addons, addon_manifests=self.addon_manager.manifests, preferences=self._preferences, locations=self._locations, proxy=self._proxy) def create_new_profile(self): """Create a new clean profile in tmp which is a simple empty folder""" profile = tempfile.mkdtemp(suffix='.mozrunner') return profile ### methods for preferences def set_preferences(self, preferences, filename='user.js'): """Adds preferences dict to profile preferences""" # append to the file prefs_file = os.path.join(self.profile, filename) f = open(prefs_file, 'a') if preferences: # note what files we've touched self.written_prefs.add(filename) if isinstance(preferences, dict): # order doesn't matter preferences = preferences.items() # write the preferences f.write('\n%s\n' % self.delimeters[0]) _prefs = [(simplejson.dumps(k), simplejson.dumps(v)) for k, v in preferences] for _pref in _prefs: f.write('user_pref(%s, %s);\n' % _pref) f.write('%s\n' % self.delimeters[1]) f.close() def pop_preferences(self, filename): """ pop the last set of preferences added returns True if popped """ lines = file(os.path.join(self.profile, filename)).read().splitlines() def last_index(_list, value): """ returns the last index of an item; this should actually be part of python code but it isn't """ for index in reversed(range(len(_list))): if _list[index] == value: return index s = last_index(lines, self.delimeters[0]) e = last_index(lines, self.delimeters[1]) # ensure both markers are found if s is None: assert e is None, '%s found without %s' % (self.delimeters[1], self.delimeters[0]) return False # no preferences found elif e is None: assert s is None, '%s found without %s' % (self.delimeters[0], self.delimeters[1]) # ensure the markers are in the proper order assert e > s, '%s found at %s, while %s found at %s' % ( self.delimeters[1], e, self.delimeters[0], s) # write the prefs cleaned_prefs = '\n'.join(lines[:s] + lines[e + 1:]) f = file(os.path.join(self.profile, 'user.js'), 'w') f.write(cleaned_prefs) f.close() return True def clean_preferences(self): """Removed preferences added by mozrunner.""" for filename in self.written_prefs: while True: if not self.pop_preferences(filename): break ### cleanup def _cleanup_error(self, function, path, excinfo): """ Specifically for windows we need to handle the case where the windows process has not yet relinquished handles on files, so we do a wait/try construct and timeout if we can't get a clear road to deletion """ try: from exceptions import WindowsError from time import sleep def is_file_locked(): return excinfo[0] is WindowsError and excinfo[1].winerror == 32 if excinfo[0] is WindowsError and excinfo[1].winerror == 32: # Then we're on windows, wait to see if the file gets unlocked # we wait 10s count = 0 while count < 10: sleep(1) try: function(path) break except: count += 1 except ImportError: # We can't re-raise an error, so we'll hope the stuff above us will throw pass def cleanup(self): """Cleanup operations for the profile.""" if self.restore: if self.create_new: if os.path.exists(self.profile): rmtree(self.profile, onerror=self._cleanup_error) else: self.clean_preferences() self.addon_manager.clean_addons() self.permissions.clean_db() __del__ = cleanup
class Profile(object): """Handles all operations regarding profile. Created new profiles, installs extensions, sets preferences and handles cleanup. :param profile: Path to the profile :param addons: String of one or list of addons to install :param addon_manifests: Manifest for addons, see http://ahal.ca/blog/2011/bulk-installing-fx-addons/ :param apps: Dictionary or class of webapps to install :param preferences: Dictionary or class of preferences :param locations: ServerLocations object :param proxy: setup a proxy :param restore: If true remove all added addons and preferences when cleaning up """ def __init__(self, profile=None, addons=None, addon_manifests=None, apps=None, preferences=None, locations=None, proxy=None, restore=True): # if true, remove installed addons/prefs afterwards self.restore = restore # prefs files written to self.written_prefs = set() # our magic markers nonce = '%s %s' % (str(time.time()), uuid.uuid4()) self.delimeters = ('#MozRunner Prefs Start %s' % nonce, '#MozRunner Prefs End %s' % nonce) # Handle profile creation self.create_new = not profile if profile: # Ensure we have a full path to the profile self.profile = os.path.abspath(os.path.expanduser(profile)) if not os.path.exists(self.profile): os.makedirs(self.profile) else: self.profile = self.create_new_profile() # set preferences if hasattr(self.__class__, 'preferences'): # class preferences self.set_preferences(self.__class__.preferences) self._preferences = preferences if preferences: # supplied preferences if isinstance(preferences, dict): # unordered preferences = preferences.items() # sanity check assert not [i for i in preferences if len(i) != 2] else: preferences = [] self.set_preferences(preferences) # set permissions self._locations = locations # store this for reconstruction self._proxy = proxy self.permissions = Permissions(self.profile, locations) prefs_js, user_js = self.permissions.network_prefs(proxy) self.set_preferences(prefs_js, 'prefs.js') self.set_preferences(user_js) # handle addon installation self.addon_manager = AddonManager(self.profile, restore=self.restore) self.addon_manager.install_addons(addons, addon_manifests) # handle webapps self.webapps = WebappCollection(profile=self.profile, apps=apps) self.webapps.update_manifests() def exists(self): """returns whether the profile exists or not""" return os.path.exists(self.profile) def reset(self): """ reset the profile to the beginning state """ self.cleanup() if self.create_new: profile = None else: profile = self.profile self.__init__(profile=profile, addons=self.addon_manager.installed_addons, addon_manifests=self.addon_manager.installed_manifests, preferences=self._preferences, locations=self._locations, proxy=self._proxy) @classmethod def clone(cls, path_from, path_to=None, **kwargs): """Instantiate a temporary profile via cloning - path: path of the basis to clone - kwargs: arguments to the profile constructor """ if not path_to: tempdir = tempfile.mkdtemp() # need an unused temp dir name rmtree(tempdir) # copytree requires that dest does not exist path_to = tempdir copytree(path_from, path_to) def cleanup_clone(fn): """Deletes a cloned profile when restore is True""" def wrapped(self): fn(self) if self.restore and os.path.exists(self.profile): rmtree(self.profile, onerror=self._cleanup_error) return wrapped c = cls(path_to, **kwargs) c.__del__ = c.cleanup = types.MethodType(cleanup_clone(cls.cleanup), c) return c def create_new_profile(self): """Create a new clean temporary profile which is a simple empty folder""" return tempfile.mkdtemp(suffix='.mozrunner') ### methods for preferences def set_preferences(self, preferences, filename='user.js'): """Adds preferences dict to profile preferences""" # append to the file prefs_file = os.path.join(self.profile, filename) f = open(prefs_file, 'a') if preferences: # note what files we've touched self.written_prefs.add(filename) # opening delimeter f.write('\n%s\n' % self.delimeters[0]) # write the preferences Preferences.write(f, preferences) # closing delimeter f.write('%s\n' % self.delimeters[1]) f.close() def pop_preferences(self, filename): """ pop the last set of preferences added returns True if popped """ path = os.path.join(self.profile, filename) with file(path) as f: lines = f.read().splitlines() def last_index(_list, value): """ returns the last index of an item; this should actually be part of python code but it isn't """ for index in reversed(range(len(_list))): if _list[index] == value: return index s = last_index(lines, self.delimeters[0]) e = last_index(lines, self.delimeters[1]) # ensure both markers are found if s is None: assert e is None, '%s found without %s' % (self.delimeters[1], self.delimeters[0]) return False # no preferences found elif e is None: assert s is None, '%s found without %s' % (self.delimeters[0], self.delimeters[1]) # ensure the markers are in the proper order assert e > s, '%s found at %s, while %s found at %s' % ( self.delimeters[1], e, self.delimeters[0], s) # write the prefs cleaned_prefs = '\n'.join(lines[:s] + lines[e + 1:]) with file(path, 'w') as f: f.write(cleaned_prefs) return True def clean_preferences(self): """Removed preferences added by mozrunner.""" for filename in self.written_prefs: if not os.path.exists(os.path.join(self.profile, filename)): # file has been deleted break while True: if not self.pop_preferences(filename): break ### cleanup def _cleanup_error(self, function, path, excinfo): """ Specifically for windows we need to handle the case where the windows process has not yet relinquished handles on files, so we do a wait/try construct and timeout if we can't get a clear road to deletion """ try: from exceptions import WindowsError from time import sleep def is_file_locked(): return excinfo[0] is WindowsError and excinfo[1].winerror == 32 if excinfo[0] is WindowsError and excinfo[1].winerror == 32: # Then we're on windows, wait to see if the file gets unlocked # we wait 10s count = 0 while count < 10: sleep(1) try: function(path) break except: count += 1 except ImportError: # We can't re-raise an error, so we'll hope the stuff above us will throw pass def cleanup(self): """Cleanup operations for the profile.""" if self.restore: if self.create_new: if os.path.exists(self.profile): rmtree(self.profile, onerror=self._cleanup_error) else: self.clean_preferences() self.addon_manager.clean_addons() self.permissions.clean_db() self.webapps.clean() __del__ = cleanup ### methods for introspection def summary(self, return_parts=False): """ returns string summarizing profile information. if return_parts is true, return the (Part_name, value) list of tuples instead of the assembled string """ parts = [('Path', self.profile)] # profile path # directory tree parts.append(('Files', '\n%s' % tree(self.profile))) # preferences for prefs_file in ('user.js', 'prefs.js'): path = os.path.join(self.profile, prefs_file) if os.path.exists(path): # prefs that get their own section # This is currently only 'network.proxy.autoconfig_url' # but could be expanded to include others section_prefs = ['network.proxy.autoconfig_url'] line_length = 80 line_length_buffer = 10 # buffer for 80 character display: length = 80 - len(key) - len(': ') - line_length_buffer line_length_buffer += len(': ') def format_value(key, value): if key not in section_prefs: return value max_length = line_length - len(key) - line_length_buffer if len(value) > max_length: value = '%s...' % value[:max_length] return value prefs = Preferences.read_prefs(path) if prefs: prefs = dict(prefs) parts.append((prefs_file, '\n%s' % ('\n'.join([ '%s: %s' % (key, format_value(key, prefs[key])) for key in sorted(prefs.keys()) ])))) # Currently hardcorded to 'network.proxy.autoconfig_url' # but could be generalized, possibly with a generalized (simple) # JS-parser network_proxy_autoconfig = prefs.get( 'network.proxy.autoconfig_url') if network_proxy_autoconfig and network_proxy_autoconfig.strip( ): network_proxy_autoconfig = network_proxy_autoconfig.strip( ) lines = network_proxy_autoconfig.replace( ';', ';\n').splitlines() lines = [line.strip() for line in lines] origins_string = 'var origins = [' origins_end = '];' if origins_string in lines[0]: start = lines[0].find(origins_string) end = lines[0].find(origins_end, start) splitline = [ lines[0][:start], lines[0][start:start + len(origins_string) - 1], ] splitline.extend( lines[0][start + len(origins_string):end].replace( ',', ',\n').splitlines()) splitline.append(lines[0][end:]) lines[0:1] = [i.strip() for i in splitline] parts.append( ('Network Proxy Autoconfig, %s' % (prefs_file), '\n%s' % '\n'.join(lines))) if return_parts: return parts retval = '%s\n' % ('\n\n'.join( ['[%s]: %s' % (key, value) for key, value in parts])) return retval __str__ = summary