def get_doc(command, indent='', format='text'): doc = inspect.getdoc(command) or '' cargs = getargspec(command) defaults = { i + '_default': j for i, j in zip(cargs.args[-len(cargs.defaults or ()):], cargs.defaults or ()) } executable = osp.basename(sys.argv[0]) if format == 'rst': base_directory_default = ' ``$HOME/casa_distro``' else: base_directory_default = casa_distro_directory() help_vars = dict(executable=executable, casa_version=__version__, base_directory_default=base_directory_default, publish_url=publish_url, indent=' ') help_vars.update(defaults) help_vars['base_directory_default'] = base_directory_default r = re.compile('{([^}]+)}') keys = set() for text in param_help.values(): keys.update(r.findall(text)) help_vars.update({k: '' for k in keys if k not in help_vars}) help_vars.update({k: v.format(**help_vars) for k, v in param_help.items()}) doc = doc.format(**help_vars) if indent: doc = '\n'.join(indent + line for line in doc.split('\n')) return formatted_help(doc, format=format)
def clean_images(distro=None, branch=None, system=None, image_version=None, version=None, name=None, type=None, image=None, verbose=False, base_directory=casa_distro_directory(), interactive=True): ''' Delete singularity images which are no longer used in any build workflow, or those listed in the "image" parameter. There are two ways of selecting the image(s): 1. filtered by environment, using the ``name`` selector, or a combination of ``distro``, ``branch``, and ``system``. 2. directly specifying a full image name, e.g.:: casa_distro clean_images image=casa-run-ubuntu-18.04.sif Parameters ---------- {distro} {branch} {system} {image_version} {version} {name} {type} {image} {base_directory} interactive default={interactive_default} ask confirmation before deleting an image {verbose} ''' images_to_update = list(iter_images(base_directory=base_directory, distro=distro, branch=branch, system=system, image_version=image_version, version=version, name=name, type=type, image=image)) print('\n'.join(['%s\t: %s' % i for i in images_to_update])) for container_type, image_name \ in iter_images(base_directory=base_directory, distro=distro, branch=branch, system=system, image_version=image_version, name=name, type=type, image=image): if interactive: confirm = interactive_input( 'delete image %s : %s [y/N]: ' % (container_type, image_name)) if confirm not in ('y', 'yes', 'Y', 'YES'): print('skip.') continue print('deleting image %s' % image_name) delete_image(container_type, image_name)
def delete(type=None, distro=None, branch=None, system=None, image_version=None, version=None, name=None, base_directory=casa_distro_directory(), interactive=True): """ Delete an existing environment. The whole environment directory will be removed and forgotten. Use with care. Image files will be left untouched - use clean_images for this. Parameters ---------- {type} {distro} {branch} {system} {image_version} {version} {name} {base_directory} interactive default={interactive_default} if true (or 1, or yes), ask confirmation interactively for each selected environment. """ interactive = check_boolean('interactive', interactive) if not interactive and type is None and distro is None and system is None \ and image_version is None and name is None: raise RuntimeError( 'Refusing to delete all environments without confirmation. ' 'Either use interactive=True, or provide an explicit pattern for ' 'environment selection parameters') for config in iter_environments(base_directory, type=type, distro=distro, branch=branch, system=system, image_version=image_version, version=version, name=name): if interactive: confirm = interactive_input( 'delete environment %s [y/N]: ' % config['name']) if confirm not in ('y', 'yes', 'Y', 'YES'): print('skip.') continue print('deleting environment %s' % config['name']) directory = config['directory'] print('rm -rf "%s"' % directory) shutil.rmtree(directory)
def shell(type=None, distro=None, branch=None, system=None, image_version=None, name=None, version=None, base_directory=casa_distro_directory(), gui=True, opengl="auto", root=False, cwd=None, env=None, image=None, container_options=[], args_list=['-norc'], verbose=None): ''' Start a bash shell in the configured container with the given pository configuration. Parameters ---------- {type} {distro} {branch} {system} {image_version} {name} {version} {base_directory} {gui} {opengl} {root} {cwd} {env} {image} {container_options} {verbose} ''' run(type=type, distro=distro, branch=branch, system=system, image_version=image_version, name=name, version=version, base_directory=base_directory, gui=gui, opengl=opengl, root=root, cwd=cwd, env=env, image=image, container_options=container_options, args_list=['/bin/bash'] + args_list, verbose=verbose)
def list_images(distro=None, branch=None, system=None, image_version=None, version=None, name=None, type=None, image='*', base_directory=casa_distro_directory(), verbose=None): '''List the locally installed container images. There are two ways of selecting the image(s): 1. filtered by environment, using the ``name`` selector, or a combination of ``distro``, ``branch``, and ``system``. 2. directly specifying a full image name, e.g.:: casa_distro list_image image=casa-run-ubuntu-18.04.sif Parameters ---------- {distro} {branch} {system} {image_version} {version} {name} {type} {image} {base_directory} {verbose} ''' images_to_update = list(iter_images(base_directory=base_directory, distro=distro, branch=branch, system=system, image_version=image_version, version=version, name=name, type=type, image=image)) print('\n'.join(['%s\t: %s' % i for i in images_to_update]))
def bv_maker(type='dev', distro=None, branch=None, system=None, image_version=None, name=None, base_directory=casa_distro_directory(), env=None, image=None, container_options=[], args_list=[], verbose=None): ''' Start a bv_maker in the configured container with the given repository configuration. Parameters ---------- {type} {distro} {branch} {system} {image_version} {name} {base_directory} {env} {image} {container_options} {verbose} ''' args_list = ['bv_maker'] + args_list return run(type=type, distro=distro, branch=branch, system=system, image_version=image_version, name=name, base_directory=base_directory, gui=False, opengl="container", env=env, image=image, container_options=container_options, args_list=args_list, verbose=verbose)
def mrun(type=None, distro=None, branch=None, system=None, image_version=None, name=None, version=None, base_directory=casa_distro_directory(), gui=True, opengl="auto", root=False, cwd=None, env=None, image=None, container_options=[], args_list=[], verbose=None): ''' Start any command in one or several container with the given repository configuration. By default, command is executed in all existing build workflows. example:: # Launch bv_maker on all build workflows using any version of Ubuntu casa_distro mrun bv_maker system=ubuntu-* Parameters ---------- {type} {distro} {branch} {system} {image_version} {name} {version} {base_directory} {gui} {opengl} {root} {cwd} {env} {image} {container_options} {verbose} ''' verbose = verbose_file(verbose) gui = check_boolean('gui', gui) root = check_boolean('root', root) if container_options: container_options = parse_list(container_options) if env: env_list = parse_list(env) try: env = dict(e.split('=') for e in env_list) except ValueError: raise ValueError('env syntax error. Should be in the shape ' '"VAR1=value1,VAR2=value2" etc.') command = args_list res = [] for config in iter_environments(base_directory, type=type, distro=distro, branch=branch, system=system, image_version=image_version, name=name, version=version): res.append(run_container(config, command=command, gui=gui, opengl=opengl, root=root, cwd=cwd, env=env, image=image, container_options=container_options, base_directory=base_directory, verbose=verbose)) if all(r == 0 for r in res): return 0 else: sys.stderr.write('Exit codes: {0}\n'.format(res)) return max(res)
def pull_image(distro=None, branch=None, system=None, image_version=None, version=None, name=None, type=None, image='*', base_directory=casa_distro_directory(), url=default_download_url, force=False, verbose=None): '''Update the container images. By default all images that are used by at least one environment are updated. There are two ways of selecting the image(s) to be downloaded: 1. filtered by environment, using the ``name`` selector, or a combination of ``distro``, ``branch``, and ``image_version``. casa_distro pull_image type=dev image_version=5.0 2. directly specifying an image name (*not* including bulild_number), e.g.:: casa_distro pull_image image=casa-run-5.0.sif Note that even though the build number does not appear in the local image name, this command will always pull the image with the highest build_number, e.g. the image that is named casa-dev-5.0-1.sif on the server will be downloaded to a file named casa-dev-5.0.sif. Parameters ---------- {distro} {branch} {system} {type} {image_version} {version} {name} {base_directory} {image} url default={url_default} URL where to download image if it is not found. force default=False force re-download of images even if they are locally present and up-to-date. {verbose} ''' verbose = verbose_file(verbose) images_to_update = list(iter_images(base_directory=base_directory, distro=distro, branch=branch, system=system, image_version=image_version, version=version, name=name, type=type, image=image)) if not images_to_update and image not in (None, '') and '*' not in image: if image.endswith('.sif') or image.endswith('.simg'): container_type = 'singularity' images_to_update = [(container_type, image)] elif image.endswith('.ova') or image.endswith('.vdi'): container_type = 'vbox' images_to_update = [(container_type, image)] if verbose: print('images_to_update:\n%s' % '\n'.join(['%s\t: %s' % i for i in images_to_update]), file=verbose) for container_type, image in images_to_update: update_container_image(container_type, image, base_directory=base_directory, verbose=verbose, url=url, force=force) if not images_to_update: print('No build workflow match selection criteria', file=sys.stderr) return 1
def run(type=None, distro=None, branch=None, system=None, image_version=None, version=None, name=None, base_directory=casa_distro_directory(), gui=True, opengl="auto", root=False, cwd=None, env=None, image=None, container_options=None, args_list=[], verbose=None): """ Start any command in a selected run or dev environment example:: casa_distro branch=master ls -als /casa Parameters ---------- {type} {distro} {branch} {system} {image_version} {name} {version} {base_directory} {gui} {opengl} {root} {cwd} {env} {image} {container_options} {verbose} """ verbose = verbose_file(verbose) gui = check_boolean('gui', gui) root = check_boolean('root', root) config = select_environment(base_directory, type=type, distro=distro, branch=branch, system=system, image_version=image_version, name=name, version=version) if container_options: container_options = parse_list(container_options) if env: env_list = parse_list(env) try: env = dict(e.split('=') for e in env_list) except ValueError: raise ValueError('env syntax error. Should be in the shape ' '"VAR1=value1,VAR2=value2" etc.') command = args_list return run_container(config, command=command, gui=gui, opengl=opengl, root=root, cwd=cwd, env=env, image=image, container_options=container_options, base_directory=base_directory, verbose=verbose)
def list_command(type=None, distro=None, branch=None, system=None, image_version=None, name=None, base_directory=casa_distro_directory(), json='no', verbose=None): '''List run or dev environments created by "setup"/"setup_dev" command. Parameters ---------- {type} {distro} {branch} {system} {image_version} {name} {base_directory} json default = {json_default} The output is written as a list of configuration dictionaries in JSON format. {verbose} ''' json_output = check_boolean('json', json) json = sys.modules['json'] verbose = verbose_file(verbose) # json parameter is hiding json module. # it is not possible to get back to a global # variable for json. Therefore, the json module is # stored in the local variable import json json_result = [] for config in iter_environments(base_directory, type=type, distro=distro, branch=branch, system=system, image_version=image_version, name=name): if json_output: json_result.append(config) else: print(config['name']) for i in ('type', 'distro', 'branch', 'version', 'system', 'image_version', 'container_type', 'image'): v = config.get(i) if v is not None: print(' %s:' % i, config[i]) overlay = config.get('overlay') if overlay: print(' writable file system:', overlay) print(' writable file system size:', size_to_string(config['overlay_size'])) print(' directory:', config['directory']) if verbose: print(' full environment:') for line in json.dumps(config, indent=2, separators=(',', ': ')).split('\n'): print(' ', line) if json_output: json.dump(json_result, sys.stdout)
def help(command=None, format='text', full=False, file=None): """ Print global help or help about a command. Parameters ---------- format format help text in a given text format. Valid values are "text" (default) for raw text, or rst (RST/sphinx format). full if ``true`` or ``yes`` or ``1``, display each subcommand parameters documentation in the general help. """ print('HELP, format:', format) full = check_boolean('full', full) if file is None: file = sys.stdout indent = '' if format == 'text': indent = ' ' * 4 if format == 'rst': base_directory_default = ' ``$HOME/casa_distro``' else: base_directory_default = casa_distro_directory() if command: command_help = get_doc(commands[command], indent=indent, format=format) print('-' * len(command), file=file) print(command, file=file) print('-' * len(command), file=file) print(command_help, file=file) else: executable = osp.basename(sys.argv[0]) help_vars = dict(executable=executable, casa_version=__version__, base_directory_default=base_directory_default, indent=' ') r = re.compile('{([^}]+)}') keys = set() for text in param_help.values(): keys.update(r.findall(text)) help_vars.update({k: '' for k in keys if k not in help_vars}) help_vars.update( {k: v.format(**help_vars) for k, v in param_help.items()}) global_help = '''\ Casa_distro is the BrainVISA suite distribution swiss knife. It allows to setup a virtual environment and launch BrainVISA software. See http://brainivsa.info/casa-distro and https://github.com/brainvisa/casa-distro for more information Version : {casa_version} usage:: {executable} <general options> <command> [<command parameters>...] general optional arguments: -v, --verbose Display as much information as possible. --version Display casa-distro version number and exit. -h, --help Display help message and exit. If used after command name, display only the help of this command. Common parameters: ================== Most commands accept more or less the same parameters. Many subcommands need :ref:`environment` selection specifications: :ref:`see the documentation on how to specify an environment <environment_options>`. {base_directory} Commands: ========='''.format(**help_vars) # noqa: E501 help_vars['indent'] = indent global_help = formatted_help(global_help, format=format) commands_summary = [global_help] for command in commands: command_doc = get_doc(commands[command], indent=indent * 2, format=format) if not full: # Split the docstring in two to remove parameters documentation # The docstring is supposed to follow the Numpy style docstring # see # https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard command_doc = re.split(r'\s*parameters\s*-+\s*', command_doc, flags=re.I)[0] commands_summary.append('\n') if format == 'rst': commands_summary.append('.. _' + command.replace('_', '-') + '-help:') commands_summary.append('') commands_summary.append(indent + '-' * len(command)) commands_summary.append(indent + command) commands_summary.append(indent + '-' * len(command)) commands_summary.append(command_doc) print('\n'.join(commands_summary), file=file)