def test_update_batch_with_plugins(self):
     self.add_plugin()
     self.write_a_function_to_pipeline_dict_file()
     pipeline_manager = self.create_pm()
     pipeline_manager.load()
     pipeline_manager.create('stuff2', a_function)
     sel = Selector()
     ivs = [
         sel.test_pipeline_manager.stuff.a_function,
         sel.test_pipeline_manager.stuff2.a_function,
     ]
     expected_b_result = ["a", "b"]
     updates = []
     for iv in ivs:
         section_path = SectionPath.from_section_str_list(
             SectionPath(iv.section_path_str)[1:])
         updates.append(
             dict(b=expected_b_result,
                  section_path_str=section_path.path_str))
     pipeline_manager.update_batch(updates)
     for iv in ivs:
         result = pipeline_manager.run(iv)
         assert result == [
             (None, OVERRIDDEN_B_RESULT),
             (None, OVERRIDDEN_B_RESULT),
             "abc",
         ]
     assert PRE_UPDATE_BATCH_COUNTER == 1
     assert POST_UPDATE_BATCH_COUNTER == 1
    def test_create_then_update_entry_for_specific_class_dict(self):
        self.write_example_class_dict_to_file()
        pipeline_manager = self.create_pm(
            specific_class_config_dicts=CLASS_CONFIG_DICT_LIST)
        pipeline_manager.load()
        sel = Selector()
        pipeline_manager.create('example_class.thing.data')
        iv = sel.test_pipeline_manager.example_class.thing.data
        expected_a_result = (1, 2)
        section_path = SectionPath.from_section_str_list(
            SectionPath(iv.section_path_str)[1:])
        pipeline_manager.update(a=expected_a_result,
                                section_path_str=section_path.path_str)
        class_folder = os.path.join(self.defaults_path, 'example_class')
        module_folder = os.path.join(class_folder, 'thing')
        class_path = os.path.join(module_folder, 'data.py')
        with open(class_path, 'r') as f:
            contents = f.read()
            self.assert_example_class_dict_config_file_contents(contents)

        ec = sel.test_pipeline_manager.example_class.thing.data
        result = pipeline_manager.run(ec)
        assert result == 'woo'

        expect_ec = ExampleClass(name='data', a=expected_a_result)
        got_ec = ec.item
        assert ec.name == expect_ec.name == got_ec.name
        assert ec.a == expect_ec.a == got_ec.a
 def test_update_no_plugins(self):
     self.write_a_function_to_pipeline_dict_file()
     pipeline_manager = self.create_pm()
     pipeline_manager.load()
     sel = Selector()
     iv = sel.test_pipeline_manager.stuff.a_function
     expected_b_result = ["a", "b"]
     section_path = SectionPath.from_section_str_list(
         SectionPath(iv.section_path_str)[1:])
     pipeline_manager.update(b=expected_b_result,
                             section_path_str=section_path.path_str)
     result = pipeline_manager.run(iv)
     assert result == (None, expected_b_result)
     assert PRE_UPDATE_COUNTER == 0
     assert POST_UPDATE_COUNTER == 0
示例#4
0
    def _get_func_or_collection(self, section_path_str: str) -> FunctionOrCollection:
        section_path = SectionPath(section_path_str)
        registrar_name = section_path[0]

        # Check for specific class dict matching name
        for registrar in self._registrars:
            if registrar.name == registrar_name:
                lookup_in_registrar_section_path = SectionPath.from_section_str_list(section_path[1:]).path_str
                if not lookup_in_registrar_section_path:
                    # Was looking up registrar collection itself
                    return registrar.collection
                # Looking up within registrar
                return registrar.get(lookup_in_registrar_section_path)

        # Try to return from general registrar
        return self._general_registrar.get(section_path_str)
示例#5
0
 def _get_section_path_str_from_section_path_str_or_view(
         self, section_path_str_or_view: 'StrOrView') -> str:
     from pyfileconf.selector.models.itemview import is_item_view
     if is_item_view(section_path_str_or_view):
         # ItemView will have PipelineManager.name as first section, must strip
         section_path = SectionPath(
             section_path_str_or_view.section_path_str)  # type: ignore
         relative_section_path = SectionPath.from_section_str_list(
             section_path[1:])
         return relative_section_path.path_str
     elif isinstance(section_path_str_or_view, str):
         return section_path_str_or_view
     else:
         raise ValueError(
             f'expected str or ItemView. Got {section_path_str_or_view} of '
             f'type {type(section_path_str_or_view)}')
 def test_create_then_update_entry_for_function(self):
     self.write_a_function_to_pipeline_dict_file()
     pipeline_manager = self.create_pm()
     pipeline_manager.load()
     sel = Selector()
     pipeline_manager.create('thing', a_function)
     iv = sel.test_pipeline_manager.thing.a_function
     expected_b_result = ['a', 'b']
     section_path = SectionPath.from_section_str_list(
         SectionPath(iv.section_path_str)[1:])
     pipeline_manager.update(b=expected_b_result,
                             section_path_str=section_path.path_str)
     module_folder = os.path.join(self.defaults_path, 'thing')
     function_path = os.path.join(module_folder, 'a_function.py')
     with open(function_path, 'r') as f:
         contents = f.read()
         self.assert_a_function_config_file_contents(contents)
     result = pipeline_manager.run(iv)
     assert result == (None, expected_b_result)
示例#7
0
    def create(self,
               section_path_str: str,
               func_or_class: Optional[Union[Callable, Type]] = None):
        """
        Create a new configuration entry dynamically rather than manually modifying dict file

        :param func_or_class: function or class to use to generate config
        :param section_path_str: section path at which the config should be stored
        :return:
        """
        logger.info(f'Creating config for {section_path_str}')
        section_path = SectionPath(section_path_str)

        if section_path[0] in self.specific_class_names:
            # Got path for a specific class dict, add to specific class dict
            if len(section_path) < 3:
                raise ValueError(
                    'when targeting a specific class dict, section path must have minimum length of '
                    '3, e.g. example_class.thing.stuff')
            if func_or_class is not None:
                raise ValueError(
                    'only pass func_or_class when targeting main pipeline dict, not specific class dict'
                )
            self._create_specific_class_dict_entry(section_path)
            full_sp = section_path
            registrar = self._registrar_dict[section_path[0]]
            klass = registrar.collection.klass
            if klass is None:
                raise ValueError(
                    f'must have specific class set, got None for class in {registrar}'
                )
            key_attr = registrar.collection.key_attr
            registrar_obj = convert_to_empty_obj_if_necessary(
                section_path[-1], klass, key_attr=key_attr)
            set_section_path_str = SectionPath.from_section_str_list(
                section_path[1:-1]).path_str
            scaffold_path_str = SectionPath.from_section_str_list(
                section_path[1:]).path_str
        else:
            # Got path for main pipeline dict, add to main pipeline dict
            if func_or_class is None:
                raise ValueError(
                    'when adding creating item in main pipeline dict, must pass function or class'
                )
            self._create_pipeline_dict_entry(section_path, func_or_class)
            name = func_or_class.__name__
            full_sp = SectionPath.join(section_path, name)
            mod, import_base = get_module_and_name_imported_from(func_or_class)
            obj_import = ObjectImportStatement([name], import_base)
            item = ast.Name(id=name)
            imports = ImportStatementContainer([obj_import])
            registrar_obj = ObjectView.from_ast_and_imports(item, imports)
            if self._general_registrar is None:
                raise ValueError('general registrar must be defined')
            registrar = self._general_registrar
            set_section_path_str = section_path_str
            scaffold_path_str = full_sp.path_str

        registrar.set(set_section_path_str, registrar_obj)
        registrar.scaffold_config_for(scaffold_path_str)
        self.reset(full_sp.path_str, allow_create=True)
        logger.debug(f'Finished creating config for {section_path_str}')
示例#8
0
    def __call__(self, *args, **kwargs):
        from pyfileconf import PipelineManager

        # When calling, assume user always wants the real item
        actual_item = self.selector._get_real_item(self.section_path_str)
        # If this happened while running another item, add to dependencies
        self._add_to_config_dependencies_if_necessary()

        # Determine whether this object is from a specific class collection
        manager = PipelineManager.get_manager_by_section_path_str(
            self.section_path_str)
        collection_name = SectionPath(self.section_path_str)[1]
        try:
            manager._registrar_dict[collection_name]
            specific_class = True
        except KeyError:
            specific_class = False

        # Handle depending on the type of item
        if isinstance(actual_item, partial):
            # Got a function in the general registrar
            func = actual_item
        elif specific_class and isinstance(actual_item,
                                           self._specific_classes):
            # Got specific registrar class
            # Need to look up the execute attribute and apply section path str
            actual_item._section_path_str = self.section_path_str
            collection = self._specific_class_collection_map[type(actual_item)]
            execute_attr = collection.execute_attr
            func = getattr(actual_item, execute_attr)
        else:
            cannot_parse_error = ValueError(
                f'could not parse actual item, expected partial, '
                f'specific class, or method of specific class. '
                f'Got {actual_item} of type {type(actual_item)}')
            orig_item: Any = None
            try:
                orig_item = actual_item.__self__
                is_bound_method = True
            except AttributeError:
                is_bound_method = False

            if is_bound_method:
                if not isinstance(orig_item, self._specific_classes):
                    # Is bound method, but not for one of defined specific classes
                    raise cannot_parse_error

                # Got specific class method
                # Add section path to original item and then set method to be called
                orig_item_sp = SectionPath.from_section_str_list(
                    SectionPath(self.section_path_str)[:-1])
                orig_item_sp_str = orig_item_sp.path_str
                orig_item._section_path_str = orig_item_sp_str
                func = actual_item
            else:
                if inspect.isclass(actual_item):
                    raise cannot_parse_error
                # Got a class object in the general registrar
                actual_item._section_path_str = self.section_path_str
                # Simply return it
                return actual_item

        result = func(*args, **kwargs)
        return result