def test_find_by_env_var_unknown(): registry = PluginRegistry() found = registry.find_requirement_by_env_var(env_var='FOO', options=None) assert found is not None assert isinstance(found, EnvVarRequirement) assert found.env_var == 'FOO' assert "EnvVarRequirement(env_var='FOO')" == repr(found)
def test_find_by_service_type_redis(): registry = PluginRegistry() found = registry.find_requirement_by_service_type(service_type='redis', env_var='MYREDIS', options=dict()) assert found is not None assert isinstance(found, RedisRequirement) assert found.env_var == 'MYREDIS' assert found.service_type == 'redis'
def test_empty_variable_treated_as_unset(): requirement = EnvVarRequirement(registry=PluginRegistry(), env_var='FOO') status = requirement.check_status(dict(FOO=''), tmp_local_state_file(), 'default', UserConfigOverrides()) assert not status assert "Environment variable FOO is not set." == status.status_description assert [] == status.logs assert [] == status.errors
def check_not_set(dirname): local_state = LocalStateFile.load_for_directory(dirname) requirement = DownloadRequirement(registry=PluginRegistry(), env_var=ENV_VAR, url='http://example.com', filename=ENV_VAR) status = requirement.check_status(dict(PROJECT_DIR=dirname), local_state, 'default', UserConfigOverrides()) assert not status assert "Environment variable {} is not set.".format(ENV_VAR) == status.status_description
def check_bad_scheme(dirname): local_state = LocalStateFile.load_for_directory(dirname) requirement = RedisRequirement(registry=PluginRegistry(), env_var="REDIS_URL") status = requirement.check_status( dict(REDIS_URL="http://example.com/"), local_state, 'default', UserConfigOverrides()) assert not status assert "REDIS_URL value 'http://example.com/' does not have 'redis:' scheme." == status.status_description
def check_missing_filename(dirname): local_state = LocalStateFile.load_for_directory(dirname) filename = '/data.zip' requirement = DownloadRequirement(registry=PluginRegistry(), env_var=ENV_VAR, url='http://localhost/data.zip', filename='data.zip') status = requirement.check_status({ENV_VAR: filename, 'PROJECT_DIR': dirname}, local_state, 'default', UserConfigOverrides()) assert not status assert 'File not found: {}'.format(filename) == status.status_description
def test_description_is_not_a_string(): problems = [] requirements = [] DownloadRequirement._parse(PluginRegistry(), varname='FOO', item=dict(url='http://example.com/', description=[]), problems=problems, requirements=requirements) assert ["'description' field for download item FOO is not a string"] == problems assert len(requirements) == 0
def test_unzip_is_not_a_bool(): problems = [] requirements = [] DownloadRequirement._parse(PluginRegistry(), varname='FOO', item=dict(url='http://example.com/', unzip=[]), problems=problems, requirements=requirements) assert ["Value of 'unzip' for download item FOO should be a boolean, not []."] == problems assert len(requirements) == 0
def test_checksum_is_not_a_string(): problems = [] requirements = [] DownloadRequirement._parse(PluginRegistry(), varname='FOO', item=dict(url='http://example.com/', md5=[]), problems=problems, requirements=requirements) assert ['Checksum value for FOO should be a string not [].'] == problems assert len(requirements) == 0
def check_missing_package(dirname): requirement = CondaEnvRequirement( registry=PluginRegistry(), env_specs=dict(default=EnvSpec( 'default', ['boguspackage', 'boguspackage2'], []))) project_dir_disable_dedicated_env(dirname) local_state = LocalStateFile.load_for_directory(dirname) environ = minimal_environ(PROJECT_DIR=dirname) status = requirement.check_status( environ, local_state, 'default', UserConfigOverrides(inherited_env=environ.get(conda_env_var))) assert "Conda environment is missing packages: boguspackage, boguspackage2" == status.status_description
def check_provide_contents(dirname): environ = dict(foo='bar') local_state_file = LocalStateFile.load_for_directory(dirname) requirement = EnvVarRequirement(PluginRegistry(), env_var="FOO") status = requirement.check_status(environ, local_state_file, 'default', UserConfigOverrides()) context = ProvideContext(environ=environ, local_state_file=local_state_file, default_env_spec_name='default', status=status, mode=PROVIDE_MODE_DEVELOPMENT) assert dict(foo='bar') == context.environ assert context.status is status
def test_description_property(): problems = [] requirements = [] DownloadRequirement._parse(PluginRegistry(), varname='FOO', item=dict(url='http://example.com/', description="hi"), problems=problems, requirements=requirements) assert [] == problems assert len(requirements) == 1 assert requirements[0].title == 'FOO' assert requirements[0].description == 'hi'
def test_use_unzip_if_url_ends_in_zip(): problems = [] requirements = [] DownloadRequirement._parse(PluginRegistry(), varname='FOO', item='http://example.com/bar.zip', problems=problems, requirements=requirements) assert [] == problems assert len(requirements) == 1 assert requirements[0].filename == 'bar' assert requirements[0].url == 'http://example.com/bar.zip' assert requirements[0].unzip
def check_provide_contents(dirname): environ = dict() local_state_file = LocalStateFile.load_for_directory(dirname) requirement = EnvVarRequirement(PluginRegistry(), env_var="FOO") status = requirement.check_status(environ, local_state_file, 'default', UserConfigOverrides()) context = ProvideContext(environ=environ, local_state_file=local_state_file, default_env_spec_name='default', status=status, mode=PROVIDE_MODE_DEVELOPMENT) with pytest.raises(IOError) as excinfo: context.ensure_service_directory("foo") assert "this is not EEXIST" in repr(excinfo.value)
def test_allow_manual_override_of_use_unzip_if_url_ends_in_zip(): problems = [] requirements = [] DownloadRequirement._parse(PluginRegistry(), varname='FOO', item=dict(url='http://example.com/bar.zip', unzip=False), problems=problems, requirements=requirements) assert [] == problems assert len(requirements) == 1 assert requirements[0].filename == 'bar.zip' assert requirements[0].url == 'http://example.com/bar.zip' assert not requirements[0].unzip
def test_no_unzip_if_url_ends_in_zip_and_filename_also_does(): problems = [] requirements = [] DownloadRequirement._parse(PluginRegistry(), varname='FOO', item=dict(url='http://example.com/bar.zip', filename='something.zip'), problems=problems, requirements=requirements) assert [] == problems assert len(requirements) == 1 assert requirements[0].filename == 'something.zip' assert requirements[0].url == 'http://example.com/bar.zip' assert not requirements[0].unzip
def check_cannot_connect(dirname): local_state = LocalStateFile.load_for_directory(dirname) requirement = RedisRequirement(registry=PluginRegistry(), env_var="REDIS_URL") can_connect_args_list = _monkeypatch_can_connect_to_socket_fails(monkeypatch) status = requirement.check_status( dict(REDIS_URL="redis://example.com:1234/"), local_state, 'default', UserConfigOverrides()) assert dict(host='example.com', port=1234, timeout_seconds=0.5) == can_connect_args_list[0] assert dict(host='localhost', port=6379, timeout_seconds=0.5) == can_connect_args_list[1] assert not status expected = "Cannot connect to Redis at redis://example.com:1234/." assert expected == status.status_description
def __init__(self, directory_path, registry): self.directory_path = directory_path if registry is None: registry = PluginRegistry() self.registry = registry self.name = None self.description = '' self.icon = None self.commands = dict() self.default_command_name = None self.project_file_count = 0 self.conda_meta_file_count = 0 self.env_specs = dict() self.default_env_spec_name = None
def check(dirname): _monkeypatch_pwd(monkeypatch, dirname) status = SimpleStatus(success=True, description='Service added.') status.requirement = RedisRequirement(PluginRegistry(), env_var='REDIS_URL', options=dict(type='redis')) _monkeypatch_add_service(monkeypatch, status) code = _parse_args_and_run_subcommand( ['conda-kapsel', 'add-service', 'redis']) assert code == 0 out, err = capsys.readouterr() assert ( 'Service added.\n' + 'Added service redis to the project file, its address will be in REDIS_URL.\n' ) == out assert '' == err
def check_provide_contents(dirname): environ = dict() local_state_file = LocalStateFile.load_for_directory(dirname) local_state_file.set_service_run_state("myservice", dict(port=42)) requirement = EnvVarRequirement(PluginRegistry(), env_var="FOO") status = requirement.check_status(environ, local_state_file, 'default', UserConfigOverrides()) context = ProvideContext(environ=environ, local_state_file=local_state_file, default_env_spec_name='default', status=status, mode=PROVIDE_MODE_DEVELOPMENT) def transform_it(state): assert 42 == state['port'] state['port'] = 43 state['foo'] = 'bar' return 1234 result = context.transform_service_run_state("myservice", transform_it) assert 1234 == result assert dict(port=43, foo='bar') == local_state_file.get_service_run_state("myservice")
def check_provide_contents(dirname): environ = dict() local_state_file = LocalStateFile.load_for_directory(dirname) requirement = EnvVarRequirement(PluginRegistry(), env_var="FOO") status = requirement.check_status(environ, local_state_file, 'default', UserConfigOverrides()) context = ProvideContext(environ=environ, local_state_file=local_state_file, default_env_spec_name='default', status=status, mode=PROVIDE_MODE_DEVELOPMENT) workpath = context.ensure_service_directory("foo") assert os.path.isdir(workpath) assert workpath.endswith("foo") parent = os.path.dirname(workpath) assert parent.endswith("services") parent = os.path.dirname(parent) assert parent == dirname # be sure we can create if it already exists workpath2 = context.ensure_service_directory("foo") assert os.path.isdir(workpath2) assert workpath == workpath2
def check_fails_while_listing_installed(dirname): def sabotaged_installed_command(prefix): from conda_kapsel.internal import conda_api raise conda_api.CondaError("sabotage!") monkeypatch.setattr('conda_kapsel.internal.conda_api.installed', sabotaged_installed_command) project_dir_disable_dedicated_env(dirname) local_state = LocalStateFile.load_for_directory(dirname) requirement = CondaEnvRequirement( registry=PluginRegistry(), env_specs=dict( default=EnvSpec('default', ['not_a_real_package'], []))) environ = minimal_environ(PROJECT_DIR=dirname) status = requirement.check_status( environ, local_state, 'default', UserConfigOverrides(inherited_env=environ.get(conda_env_var))) assert status.status_description.startswith( "Conda failed while listing installed packages in ") assert status.status_description.endswith(": sabotage!")
def test_find_by_service_type_unknown(): registry = PluginRegistry() found = registry.find_requirement_by_service_type(service_type='blah', env_var='FOO', options=dict()) assert found is None
def test_find_by_class_name_conda_env(): registry = PluginRegistry() found = registry.find_provider_by_class_name(class_name="CondaEnvProvider") assert found is not None assert isinstance(found, CondaEnvProvider)
def test_download_item_is_none_not_a_string_or_dict(): problems = [] requirements = [] DownloadRequirement._parse(PluginRegistry(), varname='FOO', item=None, problems=problems, requirements=requirements) assert ["Download name FOO should be followed by a URL string or a dictionary describing the download."] == problems assert len(requirements) == 0
def _parse_args_and_run_subcommand(argv): parser = ArgumentParser( prog="conda-kapsel", description="Actions on kapsels (runnable projects).") # future: make setup.py store our version in a version.py then use that here # parser.add_argument('-v', '--version', action='version', version='0.1') subparsers = parser.add_subparsers(help="Sub-commands") parser.add_argument('-v', '--version', action='version', version=version) def add_directory_arg(preset): preset.add_argument( '--directory', metavar='PROJECT_DIR', default='.', help= "Project directory containing kapsel.yml (defaults to current directory)" ) def add_env_spec_arg(preset): preset.add_argument('--env-spec', metavar='ENVIRONMENT_SPEC_NAME', default=None, action='store', help="An environment spec name from kapsel.yml") def add_prepare_args(preset): add_directory_arg(preset) add_env_spec_arg(preset) all_supported_modes = list(_all_ui_modes) # we don't support "ask about every single thing" mode yet. all_supported_modes.remove(UI_MODE_TEXT_ASK_QUESTIONS) preset.add_argument('--mode', metavar='MODE', default=UI_MODE_TEXT_DEVELOPMENT_DEFAULTS_OR_ASK, choices=_all_ui_modes, action='store', help="One of " + ", ".join(_all_ui_modes)) def add_env_spec_name_arg(preset): preset.add_argument( '-n', '--name', metavar='ENVIRONMENT_SPEC_NAME', action='store', help="Name of the environment spec from kapsel.yml") preset = subparsers.add_parser( 'init', help="Initialize a directory with default project configuration") add_directory_arg(preset) preset.set_defaults(main=init.main) preset = subparsers.add_parser( 'run', help="Run the project, setting up requirements first") add_prepare_args(preset) preset.add_argument('command', metavar='COMMAND_NAME', default=None, nargs='?', help="A command name from kapsel.yml") preset.add_argument('extra_args_for_command', metavar='EXTRA_ARGS_FOR_COMMAND', default=None, nargs=REMAINDER) preset.set_defaults(main=run.main) preset = subparsers.add_parser( 'prepare', help="Set up the project requirements, but does not run the project") add_prepare_args(preset) preset.set_defaults(main=prepare.main) preset = subparsers.add_parser( 'clean', help= "Removes generated state (stops services, deletes environment files, etc)" ) add_directory_arg(preset) preset.set_defaults(main=clean.main) if not conda_kapsel._beta_test_mode: preset = subparsers.add_parser( 'activate', help= "Set up the project and output shell export commands reflecting the setup" ) add_prepare_args(preset) preset.set_defaults(main=activate.main) preset = subparsers.add_parser( 'archive', help= "Create a .zip, .tar.gz, or .tar.bz2 archive with project files in it") add_directory_arg(preset) preset.add_argument('filename', metavar='ARCHIVE_FILENAME') preset.set_defaults(main=archive.main) preset = subparsers.add_parser('upload', help="Upload the project to Anaconda Cloud") add_directory_arg(preset) preset.add_argument('-s', '--site', metavar='SITE', help='Select site to use') preset.add_argument( '-t', '--token', metavar='TOKEN', help='Auth token or a path to a file containing a token') preset.add_argument('-u', '--user', metavar='USERNAME', help='User account, defaults to the current user') preset.set_defaults(main=upload.main) preset = subparsers.add_parser( 'add-variable', help="Add a required environment variable to the project") preset.add_argument('vars_to_add', metavar='VARS_TO_ADD', default=None, nargs=REMAINDER) preset.add_argument('--default', metavar='DEFAULT_VALUE', default=None, help='Default value if environment variable is unset') add_directory_arg(preset) preset.set_defaults(main=variable_commands.main_add) preset = subparsers.add_parser( 'remove-variable', help="Remove an environment variable from the project") add_directory_arg(preset) preset.add_argument('vars_to_remove', metavar='VARS_TO_REMOVE', default=None, nargs=REMAINDER) preset.set_defaults(main=variable_commands.main_remove) preset = subparsers.add_parser('list-variables', help="List all variables on the project") add_directory_arg(preset) preset.set_defaults(main=variable_commands.main_list) preset = subparsers.add_parser( 'set-variable', help="Set an environment variable value in kapsel-local.yml") preset.add_argument('vars_and_values', metavar='VARS_AND_VALUES', default=None, nargs=REMAINDER) add_directory_arg(preset) preset.set_defaults(main=variable_commands.main_set) preset = subparsers.add_parser( 'unset-variable', help="Unset an environment variable value from kapsel-local.yml") add_directory_arg(preset) preset.add_argument('vars_to_unset', metavar='VARS_TO_UNSET', default=None, nargs=REMAINDER) preset.set_defaults(main=variable_commands.main_unset) preset = subparsers.add_parser( 'add-download', help="Add a URL to be downloaded before running commands") add_directory_arg(preset) preset.add_argument('filename_variable', metavar='ENV_VAR_FOR_FILENAME', default=None) preset.add_argument('download_url', metavar='DOWNLOAD_URL', default=None) preset.add_argument( '--filename', help="The name to give the file/folder after downloading it", default=None) preset.add_argument('--hash-algorithm', help="Defines which hash algorithm to use", default=None, choices=_hash_algorithms) preset.add_argument( '--hash-value', help="The expected checksum hash of the downloaded file", default=None) preset.set_defaults(main=download_commands.main_add) preset = subparsers.add_parser( 'remove-download', help="Remove a download from the project and from the filesystem") add_directory_arg(preset) preset.add_argument('filename_variable', metavar='ENV_VAR_FOR_FILENAME', default=None) preset.set_defaults(main=download_commands.main_remove) preset = subparsers.add_parser('list-downloads', help="List all downloads on the project") add_directory_arg(preset) preset.set_defaults(main=download_commands.main_list) service_types = PluginRegistry().list_service_types() service_choices = list(map(lambda s: s.name, service_types)) def add_service_variable_name(preset): preset.add_argument('--variable', metavar='ENV_VAR_FOR_SERVICE_ADDRESS', default=None) preset = subparsers.add_parser( 'add-service', help="Add a service to be available before running commands") add_directory_arg(preset) add_service_variable_name(preset) preset.add_argument('service_type', metavar='SERVICE_TYPE', default=None, choices=service_choices) preset.set_defaults(main=service_commands.main_add) preset = subparsers.add_parser('remove-service', help="Remove a service from the project") add_directory_arg(preset) preset.add_argument('variable', metavar='SERVICE_REFERENCE', default=None) preset.set_defaults(main=service_commands.main_remove) preset = subparsers.add_parser('list-services', help="List services present in the project") add_directory_arg(preset) preset.set_defaults(main=service_commands.main_list) def add_package_args(preset): preset.add_argument('-c', '--channel', metavar='CHANNEL', action='append', help='Channel to search for packages') preset.add_argument('packages', metavar='PACKAGES', default=None, nargs=REMAINDER) preset = subparsers.add_parser( 'add-env-spec', help="Add a new environment spec to the project") add_directory_arg(preset) add_package_args(preset) add_env_spec_name_arg(preset) preset.set_defaults(main=environment_commands.main_add) preset = subparsers.add_parser( 'remove-env-spec', help="Remove an environment spec from the project") add_directory_arg(preset) add_env_spec_name_arg(preset) preset.set_defaults(main=environment_commands.main_remove) preset = subparsers.add_parser( 'list-env-specs', help="List all environment specs for the project") add_directory_arg(preset) preset.set_defaults(main=environment_commands.main_list_env_specs) preset = subparsers.add_parser( 'add-packages', help="Add packages to one or all project environments") add_directory_arg(preset) add_env_spec_arg(preset) add_package_args(preset) preset.set_defaults(main=environment_commands.main_add_packages) preset = subparsers.add_parser( 'remove-packages', help="Remove packages from one or all project environments") add_directory_arg(preset) add_env_spec_arg(preset) preset.add_argument('packages', metavar='PACKAGE_NAME', default=None, nargs='+') preset.set_defaults(main=environment_commands.main_remove_packages) preset = subparsers.add_parser( 'list-packages', help="List packages for an environment on the project") add_directory_arg(preset) add_env_spec_arg(preset) preset.set_defaults(main=environment_commands.main_list_packages) def add_command_name_arg(preset): preset.add_argument('name', metavar="NAME", help="Command name used to invoke it") preset = subparsers.add_parser('add-command', help="Add a new command to the project") add_directory_arg(preset) command_choices = list(ALL_COMMAND_TYPES) + ['ask'] command_choices.remove( "conda_app_entry") # conda_app_entry is sort of silly and may go away preset.add_argument('--type', action="store", choices=command_choices, help="Command type to add") add_command_name_arg(preset) add_env_spec_arg(preset) preset.add_argument('command', metavar="COMMAND", help="Command line or app filename to add") preset.set_defaults(main=command_commands.main) preset = subparsers.add_parser('remove-command', help="Remove a command from the project") add_directory_arg(preset) add_command_name_arg(preset) preset.set_defaults(main=command_commands.main_remove) preset = subparsers.add_parser('list-commands', help="List the commands on the project") add_directory_arg(preset) preset.set_defaults(main=command_commands.main_list) # argparse doesn't do this for us for whatever reason if len(argv) < 2: print("Must specify a subcommand.", file=sys.stderr) parser.print_usage(file=sys.stderr) return 2 # argparse exits with 2 on bad args, copy that try: args = parser.parse_args(argv[1:]) except SystemExit as e: return e.code # '--directory' is used for all subcommands now, but may not be always if 'directory' in args: args.directory = os.path.abspath(args.directory) return args.main(args)
def req(env_var, options=None): return EnvVarRequirement(registry=PluginRegistry(), env_var=env_var, options=options)
def test_requirement_repr(): requirement = EnvVarRequirement(registry=PluginRegistry(), env_var='FOO') assert "EnvVarRequirement(env_var='FOO')" == repr(requirement)
def test_requirement_status_repr(): requirement = EnvVarRequirement(registry=PluginRegistry(), env_var='FOO') status = requirement.check_status(dict(FOO=''), tmp_local_state_file(), 'default', UserConfigOverrides()) assert "RequirementStatus(False,'Environment variable FOO is not set.',EnvVarRequirement(env_var='FOO'))" == repr( status)