def register_target(name, target): """ register a target type to a given target name :param str name: the target name (case sensitive) :param target: the target to register :type target: :class:`cleanmymac.target.Target` """ global __TARGETS__ if issubclass(target, Target): debug('registering : {0}'.format(name)) __TARGETS__[name] = target else: error('target {0} is not of type Target, instead got: {1}'.format(name, target))
def get_target(name): """ get a registered target :param str name: the target name :return: the target :rtype: :class:`cleanmymac.target.Target` """ global __TARGETS__ try: return __TARGETS__[name] except KeyError: error("no target found for: {0}".format(name)) return None
def register_target(name, target): """ register a target type to a given target name :param str name: the target name (case sensitive) :param target: the target to register :type target: :class:`cleanmymac.target.Target` """ global __TARGETS__ if issubclass(target, Target): debug('registering : {0}'.format(name)) __TARGETS__[name] = target else: error('target {0} is not of type Target, instead got: {1}'.format( name, target))
def delete_dir_content(folder): """ delete all the files and directories in path :param Dir folder: a valid directory path """ assert isinstance(folder, Dir) if not os.path.isdir(folder.path): error('{0} not a directory'.format(folder.path)) return for root, dirs, files in os.walk(folder.path): for f in files: os.unlink(os.path.join(root, f)) for d in dirs: shutil.rmtree(os.path.join(root, d))
def _run(self, commands): for cmd in commands: self._debug('run command "{0}"'.format(cmd)) try: with Capture() as err: with Capture() as out: if self._verbose: echo_success('running: {0}'.format(cmd)) command_done = Event() def redirect(): while not command_done.is_set(): try: for line in out: echo_info(line.strip()) except TypeError: pass try: for line in err: warn(line.strip()) except TypeError: pass sleep(0.05) p = run(cmd, stdout=out, stderr=err, env=self._env, async=True) if self._verbose: Thread(target=redirect).start() try: p.wait() finally: # make sure the console redirect thread is properly shutting down command_done.set() except OSError: error( 'command: "{0}" could not be executed (not found?)'.format( cmd))
def load_target(yaml_file, config, update=False, verbose=False, strict=True): """ load a target given its description from a **YAML** file. The file is validated according to its type before loading. :param str yaml_file: a valid path to a **YAML** file :param dict config: the global configuration dictionary :param bool update: specify whether to perform update before cleanup :param bool verbose: toggle verbosity :return: the target :rtype: :class:`cleanmymac.target.Target` """ with open(yaml_file, 'r+') as DESC: try: description = load(DESC) description = validate_yaml_target(description, strict=strict) _type = description['type'] if _type not in VALID_TARGET_TYPES: error( 'unknown yaml target type: "{0}", valid options are: {1}'. format(_type, VALID_TARGET_TYPES)) return None target_class = __YAML_TYPES__[_type] if not issubclass(target_class, Target): error( 'expected a subclass of Target for "{0}", instead got: "{1}"' .format(os.path.basename(yaml_file), target_class)) return None if not config: config = {} config['spec'] = description['spec'] return target_class(config, update=update, verbose=verbose) except Exception as e: error('Error loading configuration: "{0}". Reason: {1}'.format( yaml_file, e)) if strict: raise e return None
def load_target(yaml_file, config, update=False, verbose=False, strict=True): """ load a target given its description from a **YAML** file. The file is validated according to its type before loading. :param str yaml_file: a valid path to a **YAML** file :param dict config: the global configuration dictionary :param bool update: specify whether to perform update before cleanup :param bool verbose: toggle verbosity :return: the target :rtype: :class:`cleanmymac.target.Target` """ with open(yaml_file, 'r+') as DESC: try: description = load(DESC) description = validate_yaml_target(description, strict=strict) _type = description['type'] if _type not in VALID_TARGET_TYPES: error('unknown yaml target type: "{0}", valid options are: {1}'.format( _type, VALID_TARGET_TYPES )) return None target_class = __YAML_TYPES__[_type] if not issubclass(target_class, Target): error('expected a subclass of Target for "{0}", instead got: "{1}"'.format( os.path.basename(yaml_file), target_class )) return None if not config: config = {} config['spec'] = description['spec'] return target_class(config, update=update, verbose=verbose) except Exception as e: error('Error loading configuration: "{0}". Reason: {1}'.format(yaml_file, e)) if strict: raise e return None
def validate_yaml_target(description, strict=True): """ performs the validation of the **YAML** definition of a :class:`cleanmymac.target.Target`. Currently two kinds of schemas are supported. * Shell command based Targets .. code-block:: yaml type: 'cmd' spec: { update_commands: [ 'conda update conda', 'conda update anaconda' ], clean_commands: [ 'conda clean -p -s -t -y' ] } * Directory based Targets .. code-block:: yaml type: 'dir' spec: { update_message: 'Get the latest Java version from http://www.oracle.com/technetwork/java/javase/downloads/index.html', entries: [ { dir: '/Library/Java/JavaVirtualMachines', pattern: 'jdk1\.6\.\d_\d+\.jdk' }, { dir: '/Library/Java/JavaVirtualMachines', pattern: 'jdk1\.7\.\d_\d+\.jdk' }, { dir: '/Library/Java/JavaVirtualMachines', pattern: 'jdk1\.8\.\d_\d+\.jdk' }, ] } :param dict description: the loaded description :param bool strict: perform strict validation (fail on invalid specification if True) :return: the validate description :rtype: dict """ global __TYPE_SCHEMA__ schema = _target_schema() description = schema(description) _type = description['type'] _spec = description['spec'] try: schema = __TYPE_SCHEMA__[_type](strict=strict) description['spec'] = schema(_spec) except Exception as e: error(e) if strict: raise e return description
def cli(update, dry_run, quiet, pretty_print, strict, list_targets, stop_on_error, config, targets_path, targets, **kwargs): """ the main **run** method, responsible for creating the parser and executing the main logic in **cleanmymac** :param bool update: perform update of targets (if applicable) :param bool dry_run: do not execute the actions, but log the result :param bool quiet: quiet mode (no output), show a progressbar instead :param bool pretty_print: enable pretty printing with colors :param bool strict: if set enforce strict(er) rules when validating targets :param bool list_targets: list the installed targets :param bool stop_on_error: abort the execution on first error :param str config: the configuration path :param str targets_path: extra targets paths :param list targets: the targets """ disable_logger('sarge') targets = tuple([target.lower() for target in targets]) set_pretty_print(pretty_print) debug_param('update', update) debug_param('dry run', dry_run) debug_param('quiet mode', quiet) debug_param('pretty print', pretty_print) debug_param('strict mode', strict) debug_param('list available targets', list_targets) debug_param('stop on error', stop_on_error) debug_param('global config path', config) debug_param('extra targets path', targets_path) debug_param('targets', targets) debug('') all_targets = dict(iter_targets()) if is_debug(): debug("Detailed information about registered targets") debug(get_targets_as_table(simple=False, fancy=False)) if dry_run: verbose = True else: verbose = not quiet debug_param('verbose', verbose) if targets: target_names = set(targets) else: target_names = set(all_targets.keys()) if update: echo_warn( 'updating may result in an increase of the total space taken by the cleanup targets', verbose=verbose) echo_info('found {0} registered cleanup targets'.format(len(all_targets)), verbose=verbose) config = get_options(path=config) # register extra targets if any for pth in _config_targets_path(config): register_yaml_targets(pth) if targets_path and os.path.isdir(targets_path): register_yaml_targets(targets_path) if list_targets: echo_warn(get_targets_as_table(simple=True, fancy=True)) else: with progressbar(verbose, all_targets.items(), label='Processing cleanup targets:', width=40) as all_targets_bar: free_space_before = get_disk_usage('/', unit=UNIT_MB).free for name, target_initializer in all_targets_bar: echo_info(_HORIZONTAL_RULE, verbose=verbose) if name not in target_names: debug('skipping target "{0}"'.format(name)) continue echo_target('\ncleaning: {0}'.format(name.upper()), verbose=verbose) target_cfg = config[name] if name in config else None debug("got target configuration: {0}".format( pformat(target_cfg))) try: target = target_initializer(target_cfg, update=update, verbose=verbose, strict=strict) if not isinstance(target, Target): error( 'expected an instance of Target, instead got: {0}'. format(target)) continue if dry_run: echo_warn(target.describe()) else: target() except Exception, ex: error( 'could not cleanup target "{0}". Reason:\n{1}'.format( name, ex)) if stop_on_error: break if not verbose: sleep( PROGRESSBAR_ADVANCE_DELAY ) # nicer progress bar display for fast executing targets free_space_after = get_disk_usage('/', unit=UNIT_MB).free if not dry_run: echo_info('\ncleanup complete', verbose=verbose) echo_success('\nfreed {0:.3f} MB of disk space'.format( free_space_after - free_space_before), verbose=True, nl=verbose)
def cli(update, dry_run, quiet, pretty_print, strict, list_targets, stop_on_error, config, targets_path, targets, **kwargs): """ the main **run** method, responsible for creating the parser and executing the main logic in **cleanmymac** :param bool update: perform update of targets (if applicable) :param bool dry_run: do not execute the actions, but log the result :param bool quiet: quiet mode (no output), show a progressbar instead :param bool pretty_print: enable pretty printing with colors :param bool strict: if set enforce strict(er) rules when validating targets :param bool list_targets: list the installed targets :param bool stop_on_error: abort the execution on first error :param str config: the configuration path :param str targets_path: extra targets paths :param list targets: the targets """ disable_logger('sarge') targets = tuple([target.lower() for target in targets]) set_pretty_print(pretty_print) debug_param('update', update) debug_param('dry run', dry_run) debug_param('quiet mode', quiet) debug_param('pretty print', pretty_print) debug_param('strict mode', strict) debug_param('list available targets', list_targets) debug_param('stop on error', stop_on_error) debug_param('global config path', config) debug_param('extra targets path', targets_path) debug_param('targets', targets) debug('') all_targets = dict(iter_targets()) if is_debug(): debug("Detailed information about registered targets") debug(get_targets_as_table(simple=False, fancy=False)) if dry_run: verbose = True else: verbose = not quiet debug_param('verbose', verbose) if targets: target_names = set(targets) else: target_names = set(all_targets.keys()) if update: echo_warn('updating may result in an increase of the total space taken by the cleanup targets', verbose=verbose) echo_info('found {0} registered cleanup targets'.format(len(all_targets)), verbose=verbose) config = get_options(path=config) # register extra targets if any for pth in _config_targets_path(config): register_yaml_targets(pth) if targets_path and os.path.isdir(targets_path): register_yaml_targets(targets_path) if list_targets: echo_warn(get_targets_as_table(simple=True, fancy=True)) else: with progressbar(verbose, all_targets.items(), label='Processing cleanup targets:', width=40) as all_targets_bar: free_space_before = get_disk_usage('/', unit=UNIT_MB).free for name, target_initializer in all_targets_bar: echo_info(_HORIZONTAL_RULE, verbose=verbose) if name not in target_names: debug('skipping target "{0}"'.format(name)) continue echo_target('\ncleaning: {0}'.format(name.upper()), verbose=verbose) target_cfg = config[name] if name in config else None debug("got target configuration: {0}".format(pformat(target_cfg))) try: target = target_initializer(target_cfg, update=update, verbose=verbose, strict=strict) if not isinstance(target, Target): error('expected an instance of Target, instead got: {0}'.format(target)) continue if dry_run: echo_warn(target.describe()) else: target() except Exception, ex: error('could not cleanup target "{0}". Reason:\n{1}'.format(name, ex)) if stop_on_error: break if not verbose: sleep(PROGRESSBAR_ADVANCE_DELAY) # nicer progress bar display for fast executing targets free_space_after = get_disk_usage('/', unit=UNIT_MB).free if not dry_run: echo_info('\ncleanup complete', verbose=verbose) echo_success('\nfreed {0:.3f} MB of disk space'.format(free_space_after - free_space_before), verbose=True, nl=verbose)