def list_plugins(): entry_point_names = [ entry_point_name for entry_point_name in pkg_resources.get_entry_map( 'cylc-flow').keys() if entry_point_name.startswith('cylc.') ] entry_point_groups = { entry_point_name: [ entry_point for entry_point in iter_entry_points(entry_point_name) if not entry_point.module_name.startswith('cylc.flow') ] for entry_point_name in entry_point_names } dists = { entry_point.dist for entry_points in entry_point_groups.values() for entry_point in entry_points } lines = [] if dists: lines.append('\nPlugins:') maxlen1 = max(len(dist.project_name) for dist in dists) + 2 maxlen2 = max(len(dist.version) for dist in dists) + 2 for dist in dists: lines.append(f' {dist.project_name.ljust(maxlen1)}' f' {dist.version.ljust(maxlen2)}' f' {dist.module_path}') lines.append('\nEntry Points:') for entry_point_name, entry_points in entry_point_groups.items(): if entry_points: lines.append(f' {entry_point_name}:') for entry_point in entry_points: lines.append(f' {entry_point}') return '\n'.join(lines)
def load(config, additional_plugins=None): additional_plugins = additional_plugins or [] entry_points = { entry_point.name: entry_point for entry_point in iter_entry_points('cylc.main_loop') } plugins = {'state': {}, 'timings': {}} for plugin_name in config['plugins'] + additional_plugins: # get plugin try: module_name = entry_points[plugin_name.replace(' ', '_')] except KeyError: raise UserInputError( f'No main-loop plugin: "{plugin_name}"\n' + ' Available plugins:\n' + indent('\n'.join(sorted(entry_points)), ' ')) # load plugin try: module = module_name.load() except Exception: raise CylcError(f'Could not load plugin: "{plugin_name}"') # load coroutines log = [] for coro_name, coro in ((coro_name, coro) for coro_name, coro in getmembers(module) if isfunction(coro) if hasattr(coro, 'main_loop')): log.append(coro_name) plugins.setdefault(coro.main_loop, {})[(plugin_name, coro_name)] = coro plugins['timings'][(plugin_name, coro_name)] = deque(maxlen=1) LOG.debug('Loaded main loop plugin "%s": %s', plugin_name + '\n', '\n'.join((f'* {x}' for x in log))) # set the initial state of the plugin plugins['state'][plugin_name] = {} # make a note of the config here for ease of reference plugins['config'] = config return plugins
workflow//*:running # All running cycles in workflow workflow//cycle/*:running # All running tasks in workflow//cycle workflow//cycle/task/*:running # All running jobs in # workflow//cycle/task ''' # because this command is not served from behind cli_function like the # other cylc commands we have to manually patch in colour support USAGE = format_shell_examples(USAGE) USAGE = cparse(USAGE) # all sub-commands # {name: entry_point} COMMANDS: dict = { entry_point.name: entry_point for entry_point in iter_entry_points('cylc.command') } # aliases for sub-commands # {alias_name: command_name} ALIASES = { 'bcast': 'broadcast', 'compare': 'diff', 'cyclepoint': 'cycle-point', 'cycletime': 'cycle-point', 'datetime': 'cycle-point', 'external-trigger': 'ext-trigger', 'get-contact': 'get-workflow-contact', 'get-cylc-version': 'get-workflow-version', 'log': 'cat-log', 'ls': 'list',
def process_plugins(fpath, opts): """Run a Cylc pre-configuration plugin. Plugins should return a dictionary containing: 'env': A dictionary of environment variables. 'template_variables': A dictionary of template variables. 'templating_detected': Where the plugin identifies a templating language this is specified here. Expected values are ``jinja2`` or ``empy``. args: fpath: Directory where the plugin will look for a config. opts: Command line options to be passed to the plugin. Returns: Dictionary in the form: extra_vars = { 'env': {}, 'template_variables': {}, 'templating_detected': None } """ # Set out blank dictionary for return: extra_vars = { 'env': {}, 'template_variables': {}, 'templating_detected': None } # Run entry point pre_configure items, trying to merge values with each.: for entry_point in iter_entry_points( 'cylc.pre_configure' ): try: # If you want it to work on sourcedirs you need to get the options # to here. plugin_result = entry_point.resolve()( srcdir=fpath, opts=opts ) except Exception as exc: # NOTE: except Exception (purposefully vague) # this is to separate plugin from core Cylc errors raise PluginError( 'cylc.pre_configure', entry_point.name, exc ) from None for section in ['env', 'template_variables']: if section in plugin_result and plugin_result[section] is not None: # Raise error if multiple plugins try to update the same keys. section_update = plugin_result.get(section, {}) keys_collision = ( extra_vars[section].keys() & section_update.keys() ) if keys_collision: raise ParsecError( f"{entry_point.name} is trying to alter " f"[{section}]{', '.join(sorted(keys_collision))}." ) extra_vars[section].update(section_update) if ( 'templating_detected' in plugin_result and plugin_result['templating_detected'] is not None and extra_vars['templating_detected'] is not None and extra_vars['templating_detected'] != plugin_result['templating_detected'] ): # Don't allow subsequent plugins with different templating_detected raise ParsecError( "Can't merge templating languages " f"{extra_vars['templating_detected']} and " f"{plugin_result['templating_detected']}" ) elif( 'templating_detected' in plugin_result and plugin_result['templating_detected'] is not None ): extra_vars['templating_detected'] = plugin_result[ 'templating_detected' ] return extra_vars