diff --git a/.pytest_cache/v/cache/lastfailed b/.pytest_cache/v/cache/lastfailed
index b930be7..de719d8 100644
--- a/.pytest_cache/v/cache/lastfailed
+++ b/.pytest_cache/v/cache/lastfailed
@@ -1,185 +1,6 @@
{
- "tests/templates/format/test_base.py::TestJoinMethod::test_if_input_dictionaries_have_no_sections_and_have_only_parameter_lines__it_will_be_processed_correctly": true,
- "tests/templates/format/test_base.py::TestJoinMethod::test_if_input_template_dictionary_has_delete_mark_for_parameter__parameter_will_be_deleted": true,
- "tests/templates/format/test_base.py::TestJoinMethod::test_if_input_template_dictionary_has_delete_mark_for_section__section_will_be_deleted": true,
- "tests/templates/format/test_base.py::TestJoinMethod::test_if_input_template_dictionary_has_replace_mark_for_section__section_will_be_deleted": true,
- "tests/templates/format/test_base.py::TestJoinMethod::test_if_inputs_are_dictionaries_with_parameters_with_same_name_in_same_section__parameters_values_in_original_dictionary_changed_to_values_from_template": true,
- "tests/templates/format/test_base.py::TestJoinMethod::test_if_inputs_are_dictionaries_with_same_sections_which_contain_different_parameters__a_section_from_the_template_added_to_the_same_section_of_original_dictionary": true,
- "tests/templates/format/test_base.py::TestJoinMethod::test_if_inputs_are_dictionaries_with_string_keys_without_any_action_marks__the_dictionaties_just_merged": true,
- "tests/templates/format/test_base.py::TestJoinMethod::test_if_inputs_are_dictionaries_with_tuple_keys_without_any_action_marks_as_their_keys__the_dictionaries_just_merged": true,
- "tests/templates/format/test_base.py::TestLogicLinesMethod::test_if_input_is_text_document_the_method_returns_list_of_its_lines": true,
- "tests/templates/format/test_base.py::TestLogicLinesMethod::test_if_lines_in_document_divided_using_backslash_as_continuation_symbol__method_returns_list_of_full_lines": true,
"tests/templates/format/test_bind.py::TestParsingMethods::test_if_comment_parameter_is_set_for_template__format_object_will_parse_comments_with_comment_symbol_from_this_parameter": true,
- "tests/templates/format/test_bind.py::TestParsingMethods::test_if_input_document_contains_blocks_and_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element": true,
- "tests/templates/format/test_bind.py::TestParsingMethods::test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_bind.py::TestParsingMethods::test_if_input_document_contains_parameters_to_delete_without_values_or_with_empty_block__the_document_object_contains_dictionary_with_item_to_delete": true,
- "tests/templates/format/test_bind.py::TestParsingMethods::test_if_input_document_contains_some_block_of_parameters__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_bind.py::TestParsingMethods::test_if_input_document_contains_some_blocks_with_similar_names__the_blocks_join_recursively": true,
- "tests/templates/format/test_bind.py::TestParsingMethods::test_if_parameters_and_blocks_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value_or_with_special_key_in_block_dictionary": true,
- "tests/templates/format/test_bind.py::TestParsingMethods::test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments": true,
- "tests/templates/format/test_bind.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_bind.py::TestParsingMethods::test_make_template": true,
- "tests/templates/format/test_compiz.py::TestParsingMethods::test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary": true,
- "tests/templates/format/test_compiz.py::TestParsingMethods::test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_compiz.py::TestParsingMethods::test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values_and_sections_to_delete__the_document_object_contains_dictionary_with_item_to_delete": true,
- "tests/templates/format/test_compiz.py::TestParsingMethods::test_if_input_document_contains_sections_with_different_names_but_different_parameters__the_parameters_merged_in_one_section": true,
- "tests/templates/format/test_compiz.py::TestParsingMethods::test_if_input_document_contains_sections_with_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element": true,
- "tests/templates/format/test_compiz.py::TestParsingMethods::test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value": true,
- "tests/templates/format/test_compiz.py::TestParsingMethods::test_if_the_ignore_comments_flag_is_set__the_parser_ignores_all_comments": true,
- "tests/templates/format/test_compiz.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_compiz.py::TestParsingMethods::test_make_template": true,
- "tests/templates/format/test_contents.py::TestParsingMethods::test_if_input_document_contains_a_few_lines_with_some_action_symbols__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_contents.py::TestParsingMethods::test_if_input_document_contains_a_few_lines_without_any_action_symbols__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_contents.py::TestParsingMethods::test_if_template_parser_flag_is_set_False__the_initialized_object_contains_correct_dictionary_for_contents_util_module": true,
- "tests/templates/format/test_contents.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_diff.py::TestExecuteMethods::test_if_diff_patch_used_for_patching_of_directories__it_changes_files_in_directories_and_adds_ones": true,
- "tests/templates/format/test_dovecot.py::TestParsingMethods::test_if_input_document_contains_blocks_and_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element": true,
- "tests/templates/format/test_dovecot.py::TestParsingMethods::test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_dovecot.py::TestParsingMethods::test_if_input_document_contains_parameters_to_delete_without_values_or_with_empty_block__the_document_object_contains_dictionary_with_item_to_delete": true,
- "tests/templates/format/test_dovecot.py::TestParsingMethods::test_if_input_document_contains_some_block_of_parameters__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_dovecot.py::TestParsingMethods::test_if_input_document_contains_some_blocks_with_similar_names__the_blocks_join_recursively": true,
- "tests/templates/format/test_dovecot.py::TestParsingMethods::test_if_parameters_and_blocks_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value_or_with_special_key_in_block_dictionary": true,
- "tests/templates/format/test_dovecot.py::TestParsingMethods::test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments": true,
- "tests/templates/format/test_dovecot.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_dovecot.py::TestParsingMethods::test_make_template": true,
- "tests/templates/format/test_json.py::TestParsingMethods::test_if_input_document_contains_just_few_parameters_and_parameter_blocks__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_json.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_json.py::TestParsingMethods::test_make_template": true,
- "tests/templates/format/test_kde.py::TestParsingMethods::test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary": true,
- "tests/templates/format/test_kde.py::TestParsingMethods::test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_kde.py::TestParsingMethods::test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values_and_sections_to_delete__the_document_object_contains_dictionary_with_item_to_delete": true,
- "tests/templates/format/test_kde.py::TestParsingMethods::test_if_input_document_contains_parameters_with_values_with_unicode_symbols__the_initialized_object_contains_correct_dictionary": true,
- "tests/templates/format/test_kde.py::TestParsingMethods::test_if_input_document_contains_sections_with_different_names_but_different_parameters__the_parameters_merged_in_one_section": true,
- "tests/templates/format/test_kde.py::TestParsingMethods::test_if_input_document_contains_sections_with_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element": true,
- "tests/templates/format/test_kde.py::TestParsingMethods::test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value": true,
- "tests/templates/format/test_kde.py::TestParsingMethods::test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments": true,
- "tests/templates/format/test_kde.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_kde.py::TestParsingMethods::test_make_template": true,
- "tests/templates/format/test_kernel.py::TestParsingMethods::test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary": true,
- "tests/templates/format/test_kernel.py::TestParsingMethods::test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_kernel.py::TestParsingMethods::test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values__the_document_object_contains_dictionary_with_item_to_delete": true,
- "tests/templates/format/test_kernel.py::TestParsingMethods::test_if_input_document_contains_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element": true,
- "tests/templates/format/test_kernel.py::TestParsingMethods::test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value": true,
- "tests/templates/format/test_kernel.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_kernel.py::TestParsingMethods::test_make_template": true,
- "tests/templates/format/test_ldap.py::TestParsingMethods::test_if_input_doc_contains_some_type_sections_with_plain_directives__object_dictionary_contains_correct_dictionary_with_directives_values_and_comments": true,
- "tests/templates/format/test_ldap.py::TestParsingMethods::test_if_input_doc_contains_some_type_sections_with_plain_directives_and_action_marks__object_dictionary_contains_correct_dictionary_with_directives_values_and_comments": true,
- "tests/templates/format/test_ldap.py::TestParsingMethods::test_if_input_document_contains_access_to_directive__the_object_s_dictionary_contains_correct_dictionary_with_list_of_access_to_parameters_and_comments": true,
- "tests/templates/format/test_ldap.py::TestParsingMethods::test_if_input_document_contains_comments_to_type_sections__the_object_s_dictionary_collect_them": true,
- "tests/templates/format/test_ldap.py::TestParsingMethods::test_if_input_document_contains_index_directives__the_object_s_dictionary_contains_correct_dictionary_with_index_elements_and_comments": true,
- "tests/templates/format/test_ldap.py::TestParsingMethods::test_if_input_document_contains_syncrepl_and_access_to_constructions_with_action_marks__object_dictionary_contains_correct_dictionary_with_action_marks": true,
- "tests/templates/format/test_ldap.py::TestParsingMethods::test_if_input_document_contains_syncrepl_directive__the_object_s_dictionary_contains_correct_dictionary_with_list_of_syncrepl_parameters_and_comments": true,
- "tests/templates/format/test_ldap.py::TestParsingMethods::test_if_logiclines_method_takes_text_with_lines_that_starts_whit_space_symbols__it_returns_joined_lines": true,
- "tests/templates/format/test_ldap.py::TestParsingMethods::test_if_template_text_contains_some_access_to_constuctions_with_same_what_value_and_without_action_marks_for_whole_constructions__they_join_in_one_access_to_construction": true,
- "tests/templates/format/test_ldap.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_openrc.py::TestParsingMethods::test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary": true,
- "tests/templates/format/test_openrc.py::TestParsingMethods::test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_openrc.py::TestParsingMethods::test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values__the_document_object_contains_dictionary_with_item_to_delete": true,
- "tests/templates/format/test_openrc.py::TestParsingMethods::test_if_input_document_contains_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element": true,
- "tests/templates/format/test_openrc.py::TestParsingMethods::test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value": true,
- "tests/templates/format/test_openrc.py::TestParsingMethods::test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments": true,
- "tests/templates/format/test_openrc.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_openrc.py::TestParsingMethods::test_make_template": true,
- "tests/templates/format/test_postfix.py::TestParsingMethods::test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary": true,
- "tests/templates/format/test_postfix.py::TestParsingMethods::test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_postfix.py::TestParsingMethods::test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values__the_document_object_contains_dictionary_with_item_to_delete": true,
- "tests/templates/format/test_postfix.py::TestParsingMethods::test_if_input_document_contains_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element": true,
- "tests/templates/format/test_postfix.py::TestParsingMethods::test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value": true,
- "tests/templates/format/test_postfix.py::TestParsingMethods::test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments": true,
- "tests/templates/format/test_postfix.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_postfix.py::TestParsingMethods::test_make_template": true,
- "tests/templates/format/test_procmail.py::TestParsingMethods::test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary": true,
- "tests/templates/format/test_procmail.py::TestParsingMethods::test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_procmail.py::TestParsingMethods::test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values__the_document_object_contains_dictionary_with_item_to_delete": true,
- "tests/templates/format/test_procmail.py::TestParsingMethods::test_if_input_document_contains_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element": true,
- "tests/templates/format/test_procmail.py::TestParsingMethods::test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value": true,
- "tests/templates/format/test_procmail.py::TestParsingMethods::test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments": true,
- "tests/templates/format/test_procmail.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_procmail.py::TestParsingMethods::test_make_template": true,
- "tests/templates/format/test_proftpd.py::TestParsingMethods::test_if_input_document_contains_blocks_and_parameters_with_action_marks__the_key_tuples_of_parameters_s_have_it_as_its_first_element_inherited": true,
- "tests/templates/format/test_proftpd.py::TestParsingMethods::test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_proftpd.py::TestParsingMethods::test_if_input_document_contains_parameters_to_delete_without_any_values_the_document_object_contains_dictionary_with_this_items_to_delete": true,
- "tests/templates/format/test_proftpd.py::TestParsingMethods::test_if_input_document_contains_some_block_of_parameters__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_proftpd.py::TestParsingMethods::test_if_parameters_and_blocks_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value_or_with_special_key_in_block_dictionary": true,
- "tests/templates/format/test_proftpd.py::TestParsingMethods::test_if_the_ignoreComments_flag_is_set__the_parser_ignores_all_comments": true,
- "tests/templates/format/test_proftpd.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_samba.py::TestParsingMethods::test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary": true,
- "tests/templates/format/test_samba.py::TestParsingMethods::test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_samba.py::TestParsingMethods::test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values_and_sections_to_delete__the_document_object_contains_dictionary_with_item_to_delete": true,
- "tests/templates/format/test_samba.py::TestParsingMethods::test_if_input_document_contains_sections_with_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element": true,
- "tests/templates/format/test_samba.py::TestParsingMethods::test_if_input_document_contains_sections_with_similar_names_but_different_parameters__the_parameters_merged_in_one_section": true,
- "tests/templates/format/test_samba.py::TestParsingMethods::test_if_input_document_parameters_contains_upper_case_symbols__it_becomes_lower_case": true,
- "tests/templates/format/test_samba.py::TestParsingMethods::test_if_joinBefore_flag_is_set__the_document_object_contains_dictionary_with_sections_added_in_the_top_of_it": true,
- "tests/templates/format/test_samba.py::TestParsingMethods::test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value": true,
- "tests/templates/format/test_samba.py::TestParsingMethods::test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments": true,
- "tests/templates/format/test_samba.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_samba.py::TestParsingMethods::test_make_template": true,
- "tests/templates/format/test_xml_gconf.py::TestParsingMethods::test_if_input_document_is_simple_gconf__the_format_object_contains_correct_dictionary": true,
- "tests/templates/format/test_xml_gconf.py::TestParsingMethods::test_if_input_document_is_simple_gconf_tree__the_format_object_contains_correct_dictionary": true,
- "tests/templates/format/test_xml_gconf.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/format/test_xml_xfce.py::TestParsingMethods::test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary": true,
- "tests/templates/format/test_xml_xfce.py::TestParsingMethods::test_joining_documents_1": true,
- "tests/templates/test_directory_processor.py::TestDirectoryProcessor::test_just_for_debug_with_package_value": true,
- "tests/templates/test_directory_processor.py::TestDirectoryProcessor::test_just_for_debug_without_package_value": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_initialized_accoding_to_dictionary_of_correct_template_parameters__the_TemplateParameters_object_contains_processed_parameters_as_its_attributes_including_default_values": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_as_dir_parameters_object_using_correct_source_parameter_with_append_link__the_object_will_be_initialized_successfully": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_as_dir_parameters_object_using_source_parameter_but_without_append_link__the_initialization_of_the_object_will_be_failed": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_using_dictionary_with_append_parameter__a_value_of_the_parameter_will_be_checked": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_using_dictionary_with_autoupdate_parameter__a_value_of_the_parameter_will_be_checked": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_using_dictionary_with_correct_chmod_parameter__the_object_will_be_initialized_successfully": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_using_dictionary_with_correct_chmod_parameter_in_its_digital_form__the_object_will_be_initialized_successfully": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_using_dictionary_with_correct_chown_parameter__the_object_will_be_initialized_successfully": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_using_dictionary_with_correct_chown_parameter_in_its_digital_form__the_object_will_be_initialized_successfully": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_using_dictionary_with_correct_force_parameter__the_object_will_be_initialized_successfully": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_using_dictionary_with_correct_source_parameter__the_object_will_be_initialized_successfully": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_using_dictionary_with_incorrect_autoupdate_parameter__the_initialization_of_the_object_will_be_failed": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_using_dictionary_with_incorrect_chown_parameter__the_initialization_of_the_object_will_be_failed": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_using_dictionary_with_incorrect_force_parameter__the_initialization_of_the_object_will_be_failed": true,
- "tests/templates/test_parameters_processor.py::TestTemplateParameters::test_if_TemplateParameters_object_is_intialized_using_source_parameter_with_unexisting_file_path__the_initialization_of_the_object_will_be_failed": true,
- "tests/templates/test_template_action.py::TestTemplateAction::test_chmod_directory": true,
- "tests/templates/test_template_action.py::TestTemplateAction::test_chown_directory": true,
- "tests/templates/test_template_action.py::TestTemplateAction::test_create_directory": true,
- "tests/templates/test_template_action.py::TestTemplateAction::test_link_directory": true,
- "tests/templates/test_template_action.py::TestTemplateAction::test_remove_directory": true,
- "tests/templates/test_template_engine.py::TestTemplateEngine::test_if_an_input_template_binded_with_datavars_module__variables_available_in_a_template": true,
- "tests/templates/test_template_engine.py::TestTemplateEngine::test_if_an_input_template_calculate_tag_contains_pkg_function_with_an_existing_package_in_its_argument__the_pkg_function_returns_version_value_of_the_package_from_package_parameter_without_any_exceptions": true,
- "tests/templates/test_template_engine.py::TestTemplateEngine::test_if_an_input_template_contains_calculate_tag_with_some_parameters__the_template_engine_object_will_collect_its_parameters": true,
- "tests/templates/test_template_engine.py::TestTemplateEngine::test_if_an_input_template_contains_condition_and_it_is_True__the_template_engine_object_will_be_initialized_without_any_exceptions": true,
- "tests/templates/test_template_engine.py::TestTemplateEngine::test_if_an_input_template_contains_env_parameter_in_which_module_name_is_assigned__the_variables_from_this_module_can_be_used_in_template_without_determining_of_their_module": true,
- "tests/templates/test_template_engine.py::TestTemplateEngine::test_if_an_input_template_contains_pkg_function_with_existing_package_as_its_argument__it_works_correctly_and_pkg_function_returns_version_value": true,
- "tests/templates/test_template_engine.py::TestTemplateEngine::test_if_an_input_template_contains_pkg_function_with_package_that_does_not_exist_in_its_argument__it_works_correctly_and_pkg_function_returns_empty_version_value": true,
- "tests/templates/test_template_engine.py::TestTemplateEngine::test_if_an_input_template_contains_pkg_function_without_any_arguments_and_without_package_parameter_in_calculate_tag__the_pkg_function_returns_empty_version_value": true,
- "tests/templates/test_template_engine.py::TestTemplateEngine::test_if_an_input_template_contains_pkg_function_without_any_arguments_but_with_package_parameter_in_calculate_tag__the_pkg_function_returns_version_value_of_the_package_from_package_parameter": true,
- "tests/templates/test_template_engine.py::TestTemplateEngine::test_if_an_input_template_contains_several_calculate_tags__the_template_engine_will_parse_them_all_and_will_contain_all_parameters_and_result_of_all_conditions": true,
- "tests/templates/test_template_engine.py::TestTemplateEngine::test_if_an_input_template_contains_several_conditions_and_it_is_False__the_template_engine_raises_ConditionFailed_exception": true,
- "tests/templates/test_template_engine.py::TestTemplateEngine::test_if_an_input_template_contains_variables_in_its_text__the_rendered_text_will_contain_values_of_this_variables": true,
- "tests/templates/test_template_engine.py::TestTemplateEngine::test_if_value_of_variable_is_set_using_save_tag__the_new_value_of_variable_can_be_used_in_template": true,
- "tests/utils/test_files.py::TestUtils::test_if_a_pipe_Process_object_uses_for_several_writes__it_successfully_executes_even_after_read": true,
- "tests/utils/test_files.py::TestUtils::test_if_a_pipe_Process_object_uses_for_several_writes_without_readings__it_successfully_executes": true,
- "tests/utils/test_files.py::TestUtils::test_if_pipe_is_executed_using_Process_object__it_has_successfully_executed": true,
- "tests/utils/test_files.py::TestUtils::test_if_single_correct_command_executed_using_Process_object__it_successfully_executes": true,
- "tests/utils/test_package.py::TestContents::test_if_PackageContents_object_contains_contents_dictionary__it_renders_CONTENTS_file_correctly": true,
- "tests/utils/test_package.py::TestContents::test_if_PackageContents_object_initialized_by_existing_package__it_contains_dictionary_of_items_from_contents_file": true,
- "tests/utils/test_package.py::TestContents::test_if_new_directory_is_added_in_contents_file_using_add_dir_method__the_PackageContents_object_renders_the_contents_file_with_new_dir": true,
- "tests/utils/test_package.py::TestContents::test_if_new_link_is_added_in_contents_file_using_add_sym_method__the_PackageContents_object_renders_the_contents_file_with_new_sym": true,
- "tests/utils/test_package.py::TestContents::test_if_new_object_is_added_in_contents_file_using_add_obj_method__the_PackageContents_object_renders_the_contents_file_with_new_obj": true,
- "tests/utils/test_package.py::TestContents::test_if_the_PackageAtom_object_parsed_a_correct_package_atom_name_but_without_a_slot_and_use_flags__the_PackageAtom_object_returns_atom_name_of_package": true,
- "tests/utils/test_package.py::TestContents::test_if_the_PackageAtom_object_parsed_a_correct_package_atom_name_with_a_slot_value__the_PackageAtom_returns_atom_name_of_package_with_this_slot": true,
- "tests/utils/test_package.py::TestContents::test_if_the_PackageAtom_object_parsed_a_correct_package_atom_name_with_a_uses_value__the_PackageAtom_object_returns_atom_name_of_package_with_this_use_flags": true,
- "tests/utils/test_package.py::TestContents::test_if_the_PackageAtom_object_parsed_a_correct_package_atom_name_with_an_empty_slot_value__the_PackageAtom_object_returns_atom_name_of_package": true,
- "tests/utils/test_package.py::TestContents::test_if_the_PackageAtom_object_parses_a_correct_package_atom_name_without_version_value_but_with_slot_value__the_PackageAtom_object_looks_for_package_with_assigned_slot_value": true,
- "tests/utils/test_package.py::TestContents::test_if_the_PackageAtom_object_parses_a_correct_package_atom_name_without_version_value_but_with_use_flags_value__the_PackageAtom_object_looks_for_package_with_assigned_use_flags": true,
- "tests/utils/test_package.py::TestContents::test_if_the_PackageAtom_object_tried_to_parse_an_correct_package_atom_name_that_matches_multiple_packages__the_PackageAtom_object_gets_info_for_package_with_older_version": true,
- "tests/utils/test_package.py::TestContents::test_if_the_PackageAtom_object_tried_to_parse_an_incorrect_package_atom_name__the_PackageAtom_object_throws_the_PackageAtomError_exception": true,
- "tests/utils/test_package.py::TestContents::test_if_the_PackageAtom_object_tried_to_parse_an_package_atom_name_with_incorrect_use_flags__the_PackageAtom_object_throws_the_PackageAtomError_exception": true,
- "tests/utils/test_package.py::TestContents::test_if_the_get_file_package_method_of_the_PackageAtom_object_is_called_with_a_name_of_a_file_that_belongs_to_any_package__the_PackageAtom_object_contains_dictionary_with_an_owner_package": true,
- "tests/utils/test_package.py::TestContents::test_if_the_get_file_package_method_of_the_PackageAtom_object_is_called_with_a_name_of_a_file_that_does_not_belong_to_any_package__the_PackageAtom_object_throws_the_PackageAtomError_exception": true,
- "tests/utils/test_package.py::TestContents::test_if_two_Version_objects_compared_using_eq_operation_and_the_left_version_value_is_less_than_or_equal_to_the_right_version__the_result_of_the_comparing_would_be_True": true,
- "tests/utils/test_package.py::TestContents::test_if_two_Version_objects_compared_using_ge_operation_and_the_left_version_value_is_less_than_or_equal_to_the_right_version__the_result_of_the_comparing_would_be_True": true,
- "tests/utils/test_package.py::TestContents::test_if_two_Version_objects_compared_using_gt_operation_and_the_left_version_value_is_less_than_the_right_version__the_result_of_the_comparing_would_be_True": true,
- "tests/utils/test_package.py::TestContents::test_if_two_Version_objects_compared_using_le_operation_and_the_left_version_value_is_less_than_or_equal_to_the_right_version__the_result_of_the_comparing_would_be_True": true,
- "tests/utils/test_package.py::TestContents::test_if_two_Version_objects_compared_using_lt_operation_and_the_left_version_value_is_less_than_the_right_version__the_result_of_the_comparing_would_be_True": true,
- "tests/utils/test_package.py::TestContents::test_if_two_Version_objects_compared_using_ne_operation_and_the_left_version_value_is_less_than_or_equal_to_the_right_version__the_result_of_the_comparing_would_be_True": true,
- "tests/vars/test_namespace.py::TestNamespace::test_init_default_path": true,
- "tests/vars/test_namespace.py::TestNamespace::test_init_empty_namespace": true
+ "tests/templates/format/test_regex.py::TestParsingMethods::test_if_input_patch_document_contains_only_regular_expressions_without_any_regex_flags__it_correctly_patches_input_document": true,
+ "tests/templates/format/test_regex.py::TestParsingMethods::test_if_input_patch_document_contains_regular_expressions_with_global_regex_flags_and_flags_as_attributes__it_correctly_patches_input_document_using_regex_flags": true,
+ "tests/templates/test_template_action.py::TestTemplateAction::test_create_directory": true
}
\ No newline at end of file
diff --git a/.pytest_cache/v/cache/nodeids b/.pytest_cache/v/cache/nodeids
index 8eb49c8..bcc168e 100644
--- a/.pytest_cache/v/cache/nodeids
+++ b/.pytest_cache/v/cache/nodeids
@@ -115,8 +115,6 @@
"tests/templates/format/test_openrc.py::TestParsingMethods::test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values__the_document_object_contains_dictionary_with_item_to_delete",
"tests/templates/format/test_openrc.py::TestParsingMethods::test_joining_documents_1",
"tests/templates/format/test_openrc.py::TestParsingMethods::test_make_template",
- "tests/templates/format/test_patch.py::TestParsingMethods::test_if_input_patch_document_contains_only_regular_expressions_without_any_regex_flags__it_correctly_patches_input_document",
- "tests/templates/format/test_patch.py::TestParsingMethods::test_if_input_patch_document_contains_regular_expressions_with_global_regex_flags_and_flags_as_attributes__it_correctly_patches_input_document_using_regex_flags",
"tests/templates/format/test_postfix.py::TestParsingMethods::test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary",
"tests/templates/format/test_postfix.py::TestParsingMethods::test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary",
"tests/templates/format/test_postfix.py::TestParsingMethods::test_if_input_document_contains_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element",
@@ -140,6 +138,8 @@
"tests/templates/format/test_proftpd.py::TestParsingMethods::test_if_the_ignoreComments_flag_is_set__the_parser_ignores_all_comments",
"tests/templates/format/test_proftpd.py::TestParsingMethods::test_if_input_document_contains_parameters_to_delete_without_any_values_the_document_object_contains_dictionary_with_this_items_to_delete",
"tests/templates/format/test_proftpd.py::TestParsingMethods::test_joining_documents_1",
+ "tests/templates/format/test_regex.py::TestParsingMethods::test_if_input_patch_document_contains_only_regular_expressions_without_any_regex_flags__it_correctly_patches_input_document",
+ "tests/templates/format/test_regex.py::TestParsingMethods::test_if_input_patch_document_contains_regular_expressions_with_global_regex_flags_and_flags_as_attributes__it_correctly_patches_input_document_using_regex_flags",
"tests/templates/format/test_samba.py::TestParsingMethods::test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary",
"tests/templates/format/test_samba.py::TestParsingMethods::test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary",
"tests/templates/format/test_samba.py::TestParsingMethods::test_if_input_document_contains_sections_with_similar_names_but_different_parameters__the_parameters_merged_in_one_section",
diff --git a/calculate/templates/format/diff_format.py b/calculate/templates/format/diff_format.py
deleted file mode 100644
index 83740a5..0000000
--- a/calculate/templates/format/diff_format.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# vim: fileencoding=utf-8
-#
-from .base_format import BaseFormat
-from ..template_engine import ParametersContainer
-from calculate.utils.files import Process
-from calculate.templates.format.base_format import FormatError
-from os import path
-
-
-class DiffFormat(BaseFormat):
- FORMAT = 'diff'
- EXECUTABLE = True
-
- def __init__(self, document_text: str,
- join_before=False,
- parameters=ParametersContainer()):
- self._patch_text = document_text
- self._cwd_path = '/'
- self._last_level = 0
-
- # Измененные файлы.
- self.changed_files = dict()
-
- def execute_format(self, target_path):
- '''Метод для запуска работы формата.'''
- self._cwd_path = target_path
- if not path.isdir(self._cwd_path):
- # Если target_path -- путь к файлу, запускаем все процессы из
- # директории, в которой этот файл находится.
- self._cwd_path = path.dirname(self._cwd_path)
-
- if not path.exists(self._cwd_path):
- raise FormatError('root path does not exist')
-
- if self._patch_text:
- self._patch_document()
- return self.changed_files
- else:
- raise FormatError('empty patch file')
-
- def _patch_document(self):
- '''Метод, производящий наложение патча путем запуска процесса patch.'''
- # Сначала определяем на каком уровне накладываем патч.
- # Для этого запускаем утилиту patch с --dry-run и смотрим результат
- # выполнения.
- for level in range(0, 4):
- patch_dry_run = Process('patch', '--dry-run',
- '-p{}'.format(level),
- cwd=self._cwd_path)
- patch_dry_run.write(self._patch_text)
- if patch_dry_run.success():
- break
-
- patch_dry_run = Process('patch', '-R', '--dry-run',
- '-p{}'.format(level),
- cwd=self._cwd_path)
- patch_dry_run.write(self._patch_text)
- if patch_dry_run.success():
- return ''
- else:
- raise FormatError('correction failed')
-
- self._last_level = level
- patch_run = Process('patch', '-p{}'.format(level),
- cwd=self._cwd_path)
- patch_run.write(self._patch_text)
-
- if patch_run.success():
- for line in patch_run.read_lines():
- if line.startswith('patching file'):
- changed_file_path = path.join(self._cwd_path,
- line[13:].strip())
- if path.exists(changed_file_path):
- self.changed_files.update({changed_file_path:
- 'modify'})
- else:
- self.changed_files.update({changed_file_path:
- 'remove'})
- print('Changings: ')
- for file_path, change_type in self.changed_files.items():
- print('{}: {}'.format(file_path, change_type))
- return patch_run.read()
- else:
- return ''
diff --git a/calculate/templates/format/patch_format.py b/calculate/templates/format/patch_format.py
index 7a8e839..de70843 100644
--- a/calculate/templates/format/patch_format.py
+++ b/calculate/templates/format/patch_format.py
@@ -1,183 +1,84 @@
# vim: fileencoding=utf-8
#
-from .base_format import BaseFormat, FormatError
+from .base_format import BaseFormat
from ..template_engine import ParametersContainer
-from collections import OrderedDict
-import re
-try:
- from lxml.etree.ElementTree import fromstring
-except ImportError:
- from xml.etree.ElementTree import fromstring
+from calculate.utils.files import Process
+from calculate.templates.format.base_format import FormatError
+from os import path
class PatchFormat(BaseFormat):
FORMAT = 'patch'
- EXECUTABLE = False
- FORMAT_PARAMETERS = {'multiline', 'dotall', 'comment'}
+ EXECUTABLE = True
def __init__(self, document_text: str,
- ignore_comments=False,
join_before=False,
parameters=ParametersContainer()):
- processing_methods = OrderedDict()
- super().__init__(processing_methods)
+ self._patch_text = document_text
+ self._cwd_path = '/'
+ self._last_level = 0
+ # Измененные файлы.
self.changed_files = dict()
- self._multiline_flag = parameters.multiline
- self._dotall_flag = parameters.dotall
- self._parsed_patch = None
+ def execute_format(self, target_path):
+ '''Метод для запуска работы формата.'''
+ self._cwd_path = target_path
+ if not path.isdir(self._cwd_path):
+ # Если target_path -- путь к файлу, запускаем все процессы из
+ # директории, в которой этот файл находится.
+ self._cwd_path = path.dirname(self._cwd_path)
- self._document_text = document_text
- self._FLAG_VALUES = {'True': True,
- 'False': False,
- 'true': True,
- 'false': False,
- '1': True,
- '0': False}
+ if not path.exists(self._cwd_path):
+ raise FormatError('root path does not exist')
- self._XML_ROOT_LINE = '\
- {0}'
-
- def _parse_patch(self, patch_text):
- '''Метод, составляющий из текста шаблона xml документ и разбирающий его
- с помощью lxml.'''
- xml_patch = self._XML_ROOT_LINE.format(patch_text.strip())
- try:
- self._parsed_patch = fromstring(xml_patch)
- except Exception:
- raise FormatError('can not parse patch document')
-
- def join_template(self, template):
- tmp_multiline = template._multiline_flag
- tmp_dotall = template._dotall_flag
-
- template._multiline_flag = self._multiline_flag
- template._dotall_flag = self._dotall_flag
-
- self._document_text = template._join(self._document_text)
-
- template._multiline_flag = tmp_multiline
- template._dotall_flag = tmp_dotall
-
- def _join(self, input_text):
- '''Метод для запуска наложения патча.'''
- self._parse_patch(self._document_text)
-
- if not input_text.strip() == '':
- self._document_to_patch = input_text
+ if self._patch_text:
+ self._patch_document()
+ return self.changed_files
else:
- return input_text
-
- if not self._patch_document(input_text):
- raise FormatError('Error: Can not run patch.')
+ raise FormatError('empty patch file')
+
+ def _patch_document(self):
+ '''Метод, производящий наложение патча путем запуска процесса patch.'''
+ # Сначала определяем на каком уровне накладываем патч.
+ # Для этого запускаем утилиту patch с --dry-run и смотрим результат
+ # выполнения.
+ for level in range(0, 4):
+ patch_dry_run = Process('patch', '--dry-run',
+ '-p{}'.format(level),
+ cwd=self._cwd_path)
+ patch_dry_run.write(self._patch_text)
+ if patch_dry_run.success():
+ break
+
+ patch_dry_run = Process('patch', '-R', '--dry-run',
+ '-p{}'.format(level),
+ cwd=self._cwd_path)
+ patch_dry_run.write(self._patch_text)
+ if patch_dry_run.success():
+ return ''
else:
- after_patch = self._document_to_patch
- self._document_to_patch = ''
-
- return after_patch
-
- def _patch_document(self, document_to_patch):
- '''Метод, обходящий теги шаблона и использующий указанные в нем
- регулярные выражения.'''
- patch_iterator = self._parsed_patch.getiterator()
- PATCH_DOCUMENT_TAGS = ('reg', 'text')
-
- patch_element = next(patch_iterator, False)
-
- if not patch_element or not patch_element.tag == 'patch':
- raise FormatError('incorrect text of the template')
-
- while True:
- for patch_tag in PATCH_DOCUMENT_TAGS:
- patch_element = next(patch_iterator, None)
-
- if patch_element is None:
- if patch_tag == 'text':
- raise FormatError('last Text '
- 'object is missed.')
+ raise FormatError('correction failed')
+
+ self._last_level = level
+ patch_run = Process('patch', '-p{}'.format(level),
+ cwd=self._cwd_path)
+ patch_run.write(self._patch_text)
+
+ if patch_run.success():
+ for line in patch_run.read_lines():
+ if line.startswith('patching file'):
+ changed_file_path = path.join(self._cwd_path,
+ line[13:].strip())
+ if path.exists(changed_file_path):
+ self.changed_files.update({changed_file_path:
+ 'modify'})
else:
- break
-
- if patch_element.tag == patch_tag:
- if patch_element.text is not None:
- element_text = patch_element.text.strip()
- if element_text == '':
- raise FormatError(
- ("Error: Incorrect text of the "
- "template: <{0}>%s{0}>").format(
- patch_tag
- ))
- else:
- raise FormatError("Error: Incorrect text of the "
- "template: <{0}>{0}>").format(
- patch_tag
- )
-
- if patch_tag == 'reg':
- dotall = patch_element.attrib.get('dotall', False)
- regex_flags = 0
-
- if 'multiline' in patch_element.attrib:
- multiline = patch_element.attrib['multiline']
- if multiline not in self._FLAG_VALUES:
- raise FormatError('invalid multiline value')
- else:
- multiline = self._FLAG_VALUES[multiline]
-
- # Если глобально флаг MULTILINE включен, но в
- # атрибутах тэга этот флаг присутствует со
- # значением False -- для этого регулярного
- # выражения этот флаг также будет False.
- multiline_global = self._multiline_flag & multiline
- else:
- multiline = False
- multiline_global = self._multiline_flag
-
- if multiline_global or multiline:
- regex_flags |= re.MULTILINE
-
- if 'dotall' in patch_element.attrib:
- dotall = patch_element.attrib['dotall']
- if dotall not in self._FLAG_VALUES:
- raise FormatError('invalid dotall value')
- else:
- dotall = self._FLAG_VALUES[dotall]
-
- # Если глобально флаг DOTALL включен, но в
- # атрибутах тэга этот флаг присутствует со
- # значением False -- для этого регулярного
- # выражения этот флаг также будет False.
- dotall_global = self._dotall_flag & dotall
- else:
- dotall = False
- dotall_global = self._dotall_flag
-
- if dotall_global or dotall:
- regex_flags |= re.DOTALL
-
- regex_expression = re.compile(element_text,
- regex_flags)
- else:
- text_for_replace = element_text
- else:
- if patch_element.tag in PATCH_DOCUMENT_TAGS:
- error_message = '<{0}> is expected, <{1}> instead.'.\
- format(patch_tag,
- patch_element.tag)
- else:
- error_message = 'unknown tag: {0}'.format(
- patch_element.tag
- )
- raise ("incorrect text of the template: {}".format(
- error_message))
- else:
- self._document_to_patch = re.sub(regex_expression,
- text_for_replace,
- self._document_to_patch)
- continue
- return True
-
- @property
- def document_text(self):
- return self._document_text
+ self.changed_files.update({changed_file_path:
+ 'remove'})
+ print('Changings: ')
+ for file_path, change_type in self.changed_files.items():
+ print('{}: {}'.format(file_path, change_type))
+ return patch_run.read()
+ else:
+ return ''
diff --git a/calculate/templates/format/regex_format.py b/calculate/templates/format/regex_format.py
new file mode 100644
index 0000000..135ca05
--- /dev/null
+++ b/calculate/templates/format/regex_format.py
@@ -0,0 +1,183 @@
+# vim: fileencoding=utf-8
+#
+from .base_format import BaseFormat, FormatError
+from ..template_engine import ParametersContainer
+from collections import OrderedDict
+import re
+try:
+ from lxml.etree.ElementTree import fromstring
+except ImportError:
+ from xml.etree.ElementTree import fromstring
+
+
+class RegexFormat(BaseFormat):
+ FORMAT = 'regex'
+ EXECUTABLE = False
+ FORMAT_PARAMETERS = {'multiline', 'dotall', 'comment'}
+
+ def __init__(self, document_text: str,
+ ignore_comments=False,
+ join_before=False,
+ parameters=ParametersContainer()):
+ processing_methods = OrderedDict()
+ super().__init__(processing_methods)
+
+ self.changed_files = dict()
+
+ self._multiline_flag = parameters.multiline
+ self._dotall_flag = parameters.dotall
+ self._parsed_patch = None
+
+ self._document_text = document_text
+ self._FLAG_VALUES = {'True': True,
+ 'False': False,
+ 'true': True,
+ 'false': False,
+ '1': True,
+ '0': False}
+
+ self._XML_ROOT_LINE = '\
+ {0}'
+
+ def _parse_patch(self, patch_text):
+ '''Метод, составляющий из текста шаблона xml документ и разбирающий его
+ с помощью lxml.'''
+ xml_patch = self._XML_ROOT_LINE.format(patch_text.strip())
+ try:
+ self._parsed_patch = fromstring(xml_patch)
+ except Exception:
+ raise FormatError('can not parse patch document')
+
+ def join_template(self, template):
+ tmp_multiline = template._multiline_flag
+ tmp_dotall = template._dotall_flag
+
+ template._multiline_flag = self._multiline_flag
+ template._dotall_flag = self._dotall_flag
+
+ self._document_text = template._join(self._document_text)
+
+ template._multiline_flag = tmp_multiline
+ template._dotall_flag = tmp_dotall
+
+ def _join(self, input_text):
+ '''Метод для запуска наложения патча.'''
+ self._parse_patch(self._document_text)
+
+ if not input_text.strip() == '':
+ self._document_to_patch = input_text
+ else:
+ return input_text
+
+ if not self._patch_document(input_text):
+ raise FormatError('Error: Can not run patch.')
+ else:
+ after_patch = self._document_to_patch
+ self._document_to_patch = ''
+
+ return after_patch
+
+ def _patch_document(self, document_to_patch):
+ '''Метод, обходящий теги шаблона и использующий указанные в нем
+ регулярные выражения.'''
+ patch_iterator = self._parsed_patch.getiterator()
+ PATCH_DOCUMENT_TAGS = ('reg', 'text')
+
+ patch_element = next(patch_iterator, False)
+
+ if not patch_element or not patch_element.tag == 'patch':
+ raise FormatError('incorrect text of the template')
+
+ while True:
+ for patch_tag in PATCH_DOCUMENT_TAGS:
+ patch_element = next(patch_iterator, None)
+
+ if patch_element is None:
+ if patch_tag == 'text':
+ raise FormatError('last Text '
+ 'object is missed.')
+ else:
+ break
+
+ if patch_element.tag == patch_tag:
+ if patch_element.text is not None:
+ element_text = patch_element.text.strip()
+ if element_text == '':
+ raise FormatError(
+ ("Error: Incorrect text of the "
+ "template: <{0}>%s{0}>").format(
+ patch_tag
+ ))
+ else:
+ raise FormatError("Error: Incorrect text of the "
+ "template: <{0}>{0}>").format(
+ patch_tag
+ )
+
+ if patch_tag == 'reg':
+ dotall = patch_element.attrib.get('dotall', False)
+ regex_flags = 0
+
+ if 'multiline' in patch_element.attrib:
+ multiline = patch_element.attrib['multiline']
+ if multiline not in self._FLAG_VALUES:
+ raise FormatError('invalid multiline value')
+ else:
+ multiline = self._FLAG_VALUES[multiline]
+
+ # Если глобально флаг MULTILINE включен, но в
+ # атрибутах тэга этот флаг присутствует со
+ # значением False -- для этого регулярного
+ # выражения этот флаг также будет False.
+ multiline_global = self._multiline_flag & multiline
+ else:
+ multiline = False
+ multiline_global = self._multiline_flag
+
+ if multiline_global or multiline:
+ regex_flags |= re.MULTILINE
+
+ if 'dotall' in patch_element.attrib:
+ dotall = patch_element.attrib['dotall']
+ if dotall not in self._FLAG_VALUES:
+ raise FormatError('invalid dotall value')
+ else:
+ dotall = self._FLAG_VALUES[dotall]
+
+ # Если глобально флаг DOTALL включен, но в
+ # атрибутах тэга этот флаг присутствует со
+ # значением False -- для этого регулярного
+ # выражения этот флаг также будет False.
+ dotall_global = self._dotall_flag & dotall
+ else:
+ dotall = False
+ dotall_global = self._dotall_flag
+
+ if dotall_global or dotall:
+ regex_flags |= re.DOTALL
+
+ regex_expression = re.compile(element_text,
+ regex_flags)
+ else:
+ text_for_replace = element_text
+ else:
+ if patch_element.tag in PATCH_DOCUMENT_TAGS:
+ error_message = '<{0}> is expected, <{1}> instead.'.\
+ format(patch_tag,
+ patch_element.tag)
+ else:
+ error_message = 'unknown tag: {0}'.format(
+ patch_element.tag
+ )
+ raise ("incorrect text of the template: {}".format(
+ error_message))
+ else:
+ self._document_to_patch = re.sub(regex_expression,
+ text_for_replace,
+ self._document_to_patch)
+ continue
+ return True
+
+ @property
+ def document_text(self):
+ return self._document_text
diff --git a/calculate/templates/template_engine.py b/calculate/templates/template_engine.py
index 35ad6e4..8e57526 100644
--- a/calculate/templates/template_engine.py
+++ b/calculate/templates/template_engine.py
@@ -508,15 +508,19 @@ class ParametersProcessor:
"""Взять uid из chroot passwd файла."""
passwd_file_path = os.path.join(self.chroot_path, 'etc/passwd')
passwd_dictionary = []
+
if os.path.exists(passwd_file_path):
with open(passwd_file_path, 'r') as passwd_file:
for line in passwd_file:
if line.startswith('#'):
continue
+
passwd_item = tuple(line.split(':')[0:3:2])
+
if (len(passwd_item) > 1 and passwd_item[0]
and passwd_item[0]):
passwd_dictionary.append(passwd_item)
+
passwd_dictionary = dict(passwd_dictionary)
return int(passwd_dictionary[user_name])
else:
@@ -527,15 +531,20 @@ class ParametersProcessor:
"""Взять gid из chroot group файла."""
group_file_path = os.path.join(self.chroot_path, 'etc/group')
group_dictionary = []
+
if os.path.exists(group_file_path):
with open(group_file_path, 'r') as group_file:
for line in group_file:
if line.startswith('#'):
continue
+
group_item = tuple(line.split(':')[0:3:2])
+
if len(group_item) > 1 and group_item[0] and group_item[1]:
group_dictionary.append(group_item)
+
group_dictionary = dict(group_dictionary)
+
if group_name in group_dictionary:
return int(group_dictionary[group_name])
else:
diff --git a/pytest.ini b/pytest.ini
index 5ce8ff6..a5a799a 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -6,14 +6,14 @@ markers =
bind: marker for running tests for bind format.
compiz: marker for running tests for compiz format.
contents: marker for running tests for contents format.
- diff: marker for running test for diff format.
+ patch: marker for running test for patch format.
dovecot: marker for running tests for devecot format.
json: marker for running tests for json format.
kde: marker for running test for kde format.
kernel: marker for running test for kernel format.
ldap: marker for running test for ldap format.
openrc: marker for running test for openrc format.
- patch: marker for running test fot patch format.
+ regex: marker for running test fot regex format.
postfix: marker for running test for postfix format.
procmail: marker for running test for procmail format.
proftpd: marker for running tests for proftpd format.
diff --git a/template_action_draft.py b/template_action_draft.py
index c5aa8d9..2aa7b18 100644
--- a/template_action_draft.py
+++ b/template_action_draft.py
@@ -471,14 +471,30 @@ class TemplateWrapper:
if self.template_type == DIR:
self.target_package.clear_dir(self.target_path)
- def add_to_contents(self, file_md5=None):
+ def add_to_contents(self, file_md5=None, file_path=None):
'''Метод для добавления целевого файла в CONTENTS.'''
if self.parameters.append == 'link':
- self.target_package.add_sym(target_path, self.parameters.source)
+ self.target_package.add_sym(self.target_path,
+ self.parameters.source)
elif self.template_type == DIR:
- self.target_package.add_dir(target_path)
+ self.target_package.add_dir(self.target_path)
elif self.template_type == FILE:
- self.target_package.add_obj(target_path, file_md5)
+ self.target_package.add_obj(self.target_path, file_md5)
+
+ def update_contents_from_list(self, changed_list: dict):
+ for file_path, mode in changed_list.items():
+ if mode == "modify":
+ if os.path.islink(file_path):
+ self.target_package.add_sym(file_path)
+ elif os.path.isdir(file_path):
+ self.target_package.add_dir(file_path)
+ elif os.path.isfile(file_path):
+ self.target_package.add_obj(file_path)
+ elif mode == "remove":
+ if os.path.islink(file_path) or os.path.isfile(file_path):
+ self.target_package.remove_obj(file_path)
+ elif os.path.isdir(file_path):
+ self.target_package.add_dir(file_path)
@classmethod
def _set_protected(cls):
@@ -658,14 +674,32 @@ class TemplateExecutor:
if template_object.target_type is not None:
self._clear_directory(template_object.target_path)
+ # Меняем права и владельца очищенной директории, если это
+ # необходимо.
+ if template_object.parameters.chmod:
+ self._chmod_directory(target_path,
+ template_object.parameters.chmod)
+
+ if template_object.parameters.chown:
+ self._chown_directory(target_path,
+ template_object.parameters.chown)
+
template_object.clear_dir_contents()
def _append_link_directory(self, template_object: TemplateWrapper):
'''Метод описывающий действия для append = "link", если шаблон --
директория. Создает ссылку на директорию, если она есть.'''
- target = template_object.parameters.source
- link_path = template_object.target_path
- self._link_directory(link_path, target)
+ self._link_directory(template_object.parameters.source,
+ template_object.target_path)
+
+ # Меняем права и владельца файла, на который указывает ссылка.
+ if template_object.parameters.chmod:
+ self._chmod_directory(template_object.parameters.source,
+ template_object.parameters.chmod)
+
+ if template_object.parameters.chown:
+ self._chmod_directory(template_object.parameters.source,
+ template_object.parameters.chown)
template_object.add_to_contents()
@@ -687,8 +721,33 @@ class TemplateExecutor:
output_path = template_object.output_path
template_format = template_object.format_class
+ print('target_path: ', template_object.target_path)
+ print('output_path: ', output_path)
- if template_object.md5_matching:
+ # Задаемся значениями chmod и chown в зависимости от наличия или
+ # отсутствия файла.
+ try:
+ chmod = template_parameters.chmod
+ if not chmod:
+ chmod = self._get_file_mode(template_object.target_path)
+ elif template_object.target_type is not None:
+ chmod = self.directory_default_parameters.get('chmod',
+ False)
+
+ chown = template_parameters.chown
+ if not chown:
+ chown = self._get_file_owner(template_object.target_path)
+ elif template_object.target_type is not None:
+ chown = self.directory_default_parameters.get('chown',
+ False)
+ except OSError:
+ raise TemplateExecutorError('No access to the directory: {}'.
+ format(template_object.target_path))
+ print('chmod: ', chmod)
+ print('chown: ', chown)
+
+ if template_object.format_class.EXECUTABLE or\
+ template_object.md5_matching:
# Действия при совпадении md5 из CONTENTS и md5 целевого файла.
output_paths = [output_path]
@@ -719,14 +778,14 @@ class TemplateExecutor:
with open(save_path, 'w') as output_file:
output_file.write(output_text)
- if template_object.parameters.chown:
- self.chown_file(save_path,
- template_object.parameters.chown,
+ # Меняем права доступа и владельца всех сохраняемых файлов,
+ # если это необходимо.
+ if chown:
+ self.chown_file(save_path, chown,
check_existation=False)
- if template_object.parameters.chmod:
- self.chmod_file(save_path,
- template_object.parameters.chmod,
+ if chmod:
+ self.chmod_file(save_path, chmod,
check_existation=False)
# Убираем все ._cfg файлы.
@@ -746,24 +805,30 @@ class TemplateExecutor:
template_object.add_to_contents(
file_md5=output_text_md5)
else:
- parsed_template.execute_format(
+ changed_files = parsed_template.execute_format(
input_text=input_text,
target_path=template_object.target_path)
# Удаляем форматный объект входного файла.
del(parsed_template)
+ # Если исполняемый формат выдал список измененных файлов для
+ # изменения CONTENTS -- обновляем CONTENTS.
+ if changed_files:
+ template_object.update_contents_from_list(changed_files)
else:
if template_object.target_type is not None and not replace:
with open(input_path, 'r') as input_file:
input_text = input_file.read()
- parsed_input = template_format(input_text)
else:
input_text = ''
+ parsed_input = template_format(input_text)
parsed_template = template_format(template_object.template_text)
+
parsed_input.join_template(parsed_template)
# Результат наложения шаблона.
output_text = parsed_input.document_text
+
# Удаляем форматный объект входного файла.
del(parsed_input)
output_text_md5 = hashlib.md5(output_text.encode()).hexdigest()
@@ -773,16 +838,18 @@ class TemplateExecutor:
with open(output_path, 'w') as output_file:
output_file.write(output_text)
- if template_object.parameters.chown:
- self.chown_file(output_path,
- template_object.parameters.chown,
+ # Меняем права доступа и владельца ._cfg????_ файлов, если
+ # это необходимо.
+ if chown:
+ self.chown_file(output_path, chown,
check_existation=False)
- if template_object.parameters.chmod:
+ if chmod:
self.chmod_file(output_file,
template_object.parameters.chmod,
check_existation=False)
+ # Обновляем CL.
self.calculate_config_file.set_files_md5(
template_object.target_path,
output_text_md5)
@@ -794,90 +861,125 @@ class TemplateExecutor:
template_object.add_to_contents(file_md5=output_text_md5)
def _append_after_file(self, template_object: TemplateWrapper):
+ '''Метод описывающий действия при append = "after", если шаблон --
+ файл. Объединяет шаблон с целевым файлом так, чтобы текст добавлялся
+ в конец файла и в конец каждой секции файла.'''
self._append_join_file(template_object, join_before=False)
def _append_before_file(self, template_object: TemplateWrapper):
+ '''Метод описывающий действия при append = "after", если шаблон --
+ файл. Объединяет шаблон с целевым файлом так, чтобы текст добавлялся
+ в начало файла и в начало каждой секции файла.'''
self._append_join_file(template_object, join_before=True)
def _append_skip_file(self, template_object: TemplateWrapper):
+ '''Метод описывающий действия при append = "skip". Пока никаких
+ действий.'''
pass
def _append_replace_file(self, template_object: TemplateWrapper):
+ '''Метод описывающий действия при append = "replace", если шаблон --
+ файл. Очищает файл и затем накладывает на него шаблон.'''
self._append_join_file(template_object, replace=True)
def _append_remove_file(self, template_object: TemplateWrapper):
+ '''Метод описывающий действия при append = "remove", если шаблон --
+ файл. Удаляет файл.'''
if template_object.target_type is not None:
self._remove_file(template_object.target_path)
template_object.remove_from_contents()
def _append_clear_file(self, template_object: TemplateWrapper):
+ '''Метод описывающий действия при append = "clear", если шаблон --
+ файл. Очищает файл.'''
if template_object.target_type is not None:
self._clear_file(template_object.target_path)
- else:
- open(template_object.target_path, 'w').close()
- template_object.add_to_contents()
+ # Меняем владельца и права доступа к очищенному файлу, если нужно.
+ if template_object.chown:
+ self.chown_file(template_object.target_path,
+ template_object.parameters.chown)
- def _append_link_file(self, template_object: TemplateWrapper):
- if template_object.target_type is not None:
- self._link_file(template_object.target_path,
- template_object.parameters.source)
+ if template_object.chmod:
+ self.chmod_file(template_object.target_path,
+ template_object.parameters.chown)
template_object.add_to_contents()
+ def _append_link_file(self, template_object: TemplateWrapper):
+ '''Метод описывающий действия при append = "link", если шаблон --
+ файл. Создает ссылку на файл, указанный в параметре source.'''
+ self._link_file(template_object.parameters.source,
+ template_object.parameters.target_path)
+
+ # Меняем права и владельца файла, на который указывает ссылка.
+ if template_object.parameters.chmod:
+ self.chmod_file(template_object.parameters.source,
+ template_object.parameters.chmod)
+
+ if template_object.parameters.chown:
+ self.chmod_file(template_object.parameters.source,
+ template_object.parameters.chown)
+
+ template_object.add_to_contents()
+
def _create_directory(self, template_object: TemplateWrapper):
'''Метод для создания директории и, при необходимости, изменения
владельца и доступа все директорий на пути к целевой.'''
target_path = template_object.target_path
template_parameters = template_object.parameters
+ # Если файл есть, но указан chmod или chown -- используем их.
if os.access(target_path, os.F_OK):
if template_parameters.chmod:
- self.chmod_directory(target_path)
+ self._chmod_directory(target_path, template_parameters.chmod)
- if self.template_parameters.chown:
- self.chown_directory(target_path)
+ if template_parameters.chown:
+ self._chown_directory(target_path, template_parameters.chown)
return
directories_to_create = [target_path]
directory_path = os.path.dirname(target_path)
+ # Составляем список путей к директориям, которые нужно создать.
while not os.access(directory_path, os.F_OK) and directory_path:
directories_to_create.append(directory_path)
directory_path = os.path.dirname(directory_path)
+ # получаем информацию о владельце и правах доступа ближайшей
+ # существующей директории.
try:
- current_mod, current_uid, current_gid = self.get_file_info(
- directory_path,
- 'all')
- current_owner = {'uid': current_uid, 'gid': current_gid}
+ chmod = template_parameters.chmod
+ if not chmod:
+ chmod = self._get_file_mode(directory_path)
+ else:
+ chmod = self.directory_default_parameters.get('chmod', False)
+
+ chown = template_parameters.chown
+ if not chown:
+ chown = self._get_file_owner(directory_path)
+ else:
+ chown = self.directory_default_parameters.get('chown', False)
except OSError:
- raise TemplateExecutorError('No access to the directory: {}'.
- format(directory_path))
+ raise TemplateExecutorError('No access to the directory: {}'.
+ format(directory_path))
directories_to_create.reverse()
+ # создаем директории.
for create_path in directories_to_create:
try:
os.mkdir(create_path)
- if (template_parameters.chmod and
- template_parameters.chmod != current_mod):
- self.chmod_directory(create_path)
- elif 'chmod' in self.directory_default_parameters:
- self.chmod_directory(
- create_path,
- chmod_value=self.directory_default_parameters['chown'])
-
- if (template_parameters.chown and
- template_parameters.chown != current_owner):
- self.chown_directory(create_path)
- elif 'chown' in self.directory_default_parameters:
- self.chown_directory(
- create_path,
- chown_value=self.directory_default_parameters['chmod'])
+ # Для каждой созданной директории меняем права и владельца
+ # если это необходимо.
+ if chmod:
+ self._chmod_directory(create_path, chmod)
+
+ if chown:
+ self._chown_directory(create_path, chown)
except OSError as error:
raise TemplateExecutorError(
@@ -911,11 +1013,13 @@ class TemplateExecutor:
'''Метод для очистки содержимого целевой директории.'''
if os.path.exists(target_path):
if os.path.isdir(target_path):
+ # Удаляем все содержимое директории.
for node in os.scandir(target_path):
if node.is_dir():
self._remove_directory(node.path)
else:
self._remove_file(node.path)
+ return
else:
error_message = "target file is not directory"
else:
@@ -925,7 +1029,7 @@ class TemplateExecutor:
" reason: {}").format(target_path,
error_message))
- def _link_directory(self, target_path, source):
+ def _link_directory(self, source, target_path):
'''Метод для создания по целевому пути ссылки на директорию
расположенную на пути, указанному в source.'''
try:
@@ -958,7 +1062,7 @@ class TemplateExecutor:
raise TemplateExecutorError("failed to clear the file: {}".
format(target_path))
- def _link_file(self, target_path, source):
+ def _link_file(self, source, target_path):
'''Метод для создания по целевому пути ссылки на файл расположенный на
пути, указанному в source.'''
try:
@@ -968,52 +1072,6 @@ class TemplateExecutor:
"Failed to create symlink to the file: {0} -> {1}".
format(target_path, self.source))
- def chown_directory(self, target_path, chown_value={}):
- """Метод для смены владельца директории."""
- if not chown_value:
- chown_value = self.template_parameters.chown
- print('chown value = {}'.format(chown_value))
- try:
- os.chown(target_path, chown_value['uid'], chown_value['gid'])
- except (OSError, Exception) as error:
- # возможно потребуются дополнительные проверки.
- raise TemplateExecutorError(
- 'Can not chown directory: {0}, reason: {1}'.
- format(target_path, str(error)))
-
- def chmod_directory(self, target_path, chmod_value=False):
- '''Метод для смены прав доступа к директории.'''
- if not chmod_value:
- chmod_value = self.template_parameters.chmod
- try:
- os.chmod(target_path, chmod_value)
- except (OSError, Exception) as error:
- # возможно потребуются дополнительные проверки.
- self.output.set_error('Can not chmod directory: {0}, reason: {1}'.
- format(target_path, str(error)))
-
- def chown_file(self, target_path, chown_value, check_existation=True):
- '''Метод для смены владельца файла.'''
- try:
- if check_existation and not os.path.exists(target_path):
- open(target_path, 'w').close()
- os.lchown(target_path, chown_value['uid'], chown_value['gid'])
- except (OSError, Exception) as error:
- # возможно потребуются дополнительные проверки.
- raise TemplateExecutorError('Can not chown file: {0}, reason: {1}'.
- format(target_path, str(error)))
-
- def chmod_file(self, target_path, chmod_value, check_existation=True):
- '''Метод для смены прав доступа к директории.'''
- try:
- if check_existation and not os.path.exists(target_path):
- open(target_path, 'w').close()
- os.chmod(target_path, chmod_value)
- except (OSError, Exception) as error:
- # возможно потребуются дополнительные проверки.
- raise TemplateExecutorError('Can not chmod file: {0}, reason: {1}'.
- format(target_path, str(error)))
-
def _run_template(self, template_object: TemplateWrapper):
'''Метод для сохранения текста шаблонов, который должен быть исполнен
интерпретатором указанным в run прямо во время обработки шаблонов.'''
@@ -1045,18 +1103,24 @@ class TemplateExecutor:
'''
text_to_run = template_object.template_text
interpreter = template_object.parameters.exec
+
+ # Получаем путь к директории для хранения файлов .execute.
if (self.chroot_path != '/' and not
self.exec_files_directory.startswith(self.chroot_path)):
exec_files_directory = join_paths(self.chroot_path,
'/var/lib/calculate/.execute/')
+ # Если директория уже существует получаем номер очередного файла для
+ # exec по номеру последнего.
exec_number = 0
if os.path.exists(exec_files_directory):
exec_files_list = os.listdir(exec_files_directory)
if exec_files_list:
exec_number = int(exec_files_list[-1][-4:])
+
exec_number = str(exec_number + 1)
+ # Получаем название нового exec_???? файла.
if len(exec_number) < 4:
exec_number = '0' * (4 - len(exec_number)) + exec_number
exec_file_name = 'exec_{}'.format(exec_number)
@@ -1067,63 +1131,119 @@ class TemplateExecutor:
exec_file.write(text_to_run)
exec_file.close()
+ # Добавляем новый файл в словарь файлов для дальнейшего исполнения.
self.executor_output['exec_file'] = {interpreter: exec_file_name}
- def get_file_info(self, path, info='all'):
- file_stat = os.stat(path)
- if info == 'all':
- return stat.S_IMODE(file_stat.st_mode), file_stat.st_uid,\
- file_stat.st_gid
- if info == 'mode':
- return stat.S_IMODE(file_stat.st_mode)
- if info == 'owner':
- return file_stat.st_uid, file_stat.st_gid
-
- def set_uid_gid_error(self, path, uid, gid, template_path=''):
+ def _chown_directory(self, target_path, chown_value):
+ """Метод для смены владельца директории."""
+ try:
+ os.chown(target_path, chown_value['uid'], chown_value['gid'])
+ except (OSError, Exception) as error:
+ if not self._check_os_error(error, target_path):
+ raise TemplateExecutorError(
+ 'Can not chown file: {0} to {1}, reason: {2}'.
+ format(target_path, self._translate_uid_gid(
+ target_path,
+ chown_value['uid'],
+ chown_value['gid']),
+ str(error)))
+
+ def _chmod_directory(self, target_path, chmod_value):
+ '''Метод для смены прав доступа к директории.'''
+ try:
+ os.chmod(target_path, chmod_value)
+ except (OSError, Exception) as error:
+ if not self._check_os_error(error, target_path):
+ self.output.set_error(
+ 'Can not chmod directory: {0}, reason: {1}'.
+ format(target_path, str(error)))
+
+ def chown_file(self, target_path, chown_value, check_existation=True):
+ '''Метод для смены владельца файла.'''
+ try:
+ if check_existation and not os.path.exists(target_path):
+ open(target_path, 'w').close()
+ os.lchown(target_path, chown_value['uid'], chown_value['gid'])
+ except (OSError, Exception) as error:
+ if not self._check_os_error(error, target_path):
+ raise TemplateExecutorError(
+ 'Can not chown file: {0} to {1}, reason: {2}'.
+ format(target_path, self._translate_uid_gid(
+ target_path,
+ chown_value['uid'],
+ chown_value['gid']),
+ str(error)))
+
+ def chmod_file(self, target_path, chmod_value, check_existation=True):
+ '''Метод для смены прав доступа к директории.'''
+ try:
+ if check_existation and not os.path.exists(target_path):
+ open(target_path, 'w').close()
+ os.chmod(target_path, chmod_value)
+ except (OSError, Exception) as error:
+ if not self._check_os_error(error, target_path):
+ raise TemplateExecutorError(
+ 'Can not chmod file: {0}, reason: {1}'.
+ format(target_path, str(error)))
+
+ def _get_file_mode(self, file_path):
+ '''Метод для получения прав доступа для указанного файла.'''
+ file_stat = os.stat(file_path)
+ return stat.S_IMODE(file_stat.st_mode)
+
+ def _get_file_owner(self, file_path):
+ '''Метод для получения uid и gid значений для владельца указанного
+ файла.'''
+ file_stat = os.stat(file_path)
+ return {'uid': file_stat.st_uid, 'gid': file_stat.st_gid}
+
+ def _translate_uid_gid(self, target_path, uid, gid):
+ '''Метод для получения из uid и gid имен пользователя и группы при,
+ необходимых для выдачи сообщения об ошибке при попытке chown.'''
import pwd
import grp
+
try:
- user_name = pwd.getpwuid(uid).pw_name
+ if self.chroot_path == '/':
+ user_name = pwd.getpwuid(uid).pw_name
+ else:
+ user_name = str(uid)
except (TypeError, KeyError):
user_name = str(uid)
+
try:
- group_name = grp.getgrgid(gid).gr_name
+ if self.chroot_path == '/':
+ group_name = grp.getgrgid(gid).gr_name
+ else:
+ user_name = str(gid)
except (TypeError, KeyError):
group_name = str(gid)
- owner = '{0}:{1}'.format(user_name, group_name)
- if template_path:
- self.output.set_error('Failed to process template file {}'.
- template_path)
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- # !! описать ошибку !!
- # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- self.output.set_error('error with owner: {}'.format(owner))
-
- def check_filesystem(self, target_path):
- '''Метод, который предположительно будет использоваться для проверки
- файловой системы перед применением шаблона.'''
- pass
- def is_vfat(self, path):
+ return '{0}:{1}'.format(user_name, group_name)
+
+ def _check_os_error(self, error, path_to_check):
+ '''Метод для проверки причины, по которой не удалось изменить владельца
+ или права доступа файла.'''
+ if hasattr(error, 'errno') and error.errno == os.errno.EPERM:
+ if self.is_vfat(path_to_check):
+ return True
+
+ return hasattr(error, 'errno') and error.errno == os.errno.EACCES and\
+ 'var/calculate/remote' in path_to_check
+
+ def _is_vfat(self, path_to_check):
'''Метод, проверяющий является ли файловая система vfat. Нужно для того,
- чтобы заранее знать о возможности применения chown, chmod и т.д.'''
+ чтобы знать о возможности применения chown, chmod и т.д.'''
+ # Инициализируем объект для проверки примонтированных файловых систем.
if self.mounts is None:
self.mounts = Mounts()
- if self.mounts.get_from_fstab(what=self.mounts.TYPE,
- where=self.mounts.DIR,
- is_in=path) in ('vfat', 'ntfs-3g',
- 'ntfs'):
- return True
- return False
-
- def check_os_error(self, error, path):
- if hasattr(error, 'errno') and error.errno == os.errno.EPERM:
- if self.is_vfat(path):
- return True
- if hasattr(error, 'errno') and error.errno == os.errno.EACCES and\
- 'var/calculate/remote' in path:
- return True
- return False
+
+ # Проверяем файловую систему на пути.
+ return self.mounts.get_from_fstab(what=self.mounts.TYPE,
+ where=self.mounts.DIR,
+ is_in=path_to_check) in {'vfat',
+ 'ntfs-3g',
+ 'ntfs'}
# Применение основного шаблона:
diff --git a/tests/templates/format/test_diff.py b/tests/templates/format/test_diff.py
index 65a3470..7a21a15 100644
--- a/tests/templates/format/test_diff.py
+++ b/tests/templates/format/test_diff.py
@@ -1,11 +1,11 @@
import pytest
-from calculate.templates.format.diff_format import DiffFormat
+from calculate.templates.format.patch_format import PatchFormat
from calculate.utils.files import Process
from os import path
import os
-@pytest.mark.diff
+@pytest.mark.patch
class TestExecuteMethods:
def test_if_diff_patch_used_for_patching_of_several_files__it_changes_patched_file_correctly(self):
test_result = True
@@ -13,7 +13,7 @@ class TestExecuteMethods:
with open(path.join(cwd_path, 'diff_1.patch')) as patch_file:
patch_text = patch_file.read()
- diff_patch = DiffFormat(patch_text)
+ diff_patch = PatchFormat(patch_text)
print('Path:', cwd_path)
output = diff_patch.execute_format(target_path=cwd_path)
print('Output:')
@@ -51,14 +51,14 @@ class TestExecuteMethods:
def test_if_diff_patch_used_for_patching_of_directories__it_changes_files_in_directories_and_adds_ones(self):
test_result = True
cwd_path = path.join(os.getcwd(),
- 'tests/templates/format/testfiles/a1')
+ 'tests/templates/format/testfiles/a1')
patch_path = path.join(os.getcwd(),
'tests/templates/format/testfiles/diff_2.patch')
with open(path.join(patch_path)) as patch_file:
patch_text = patch_file.read()
- diff_patch = DiffFormat(patch_text)
- output = diff_patch.execute_format(target_path=cwd_path)
+ diff_patch = PatchFormat(patch_text)
+ diff_patch.execute_format(target_path=cwd_path)
for changed_file, change_type in\
diff_patch.changed_files.items():
@@ -73,15 +73,18 @@ class TestExecuteMethods:
'b1', changed_file)
with open(other_file_path) as other_file:
other_file_text = other_file.read()
- test_result = test_result and (other_file_text == patched_file_text)
+ print('test_result =', test_result)
+ test_result = test_result and (
+ other_file_text == patched_file_text)
+ print('test_result =', test_result)
if not test_result:
- # print('Differences:')
+ print('Differences:')
try:
diff_process = Process('diff', '-u',
file_path,
other_file_path)
diff_result = diff_process.read()
- # print(diff_result)
+ print(diff_result)
except Exception as error:
print('diff was not executed.')
print('Reason:', str(error))
@@ -90,11 +93,10 @@ class TestExecuteMethods:
'-p{}'.format(diff_patch._last_level),
cwd=cwd_path)
reverse_patch_run.write(patch_text)
- output = reverse_patch_run.read()
+ reverse_patch_run.read()
if reverse_patch_run.success():
print('[*] Changes was returned...')
else:
print('[!] Changes was not returned...')
assert test_result
- assert False
diff --git a/tests/templates/format/test_patch.py b/tests/templates/format/test_regex.py
similarity index 89%
rename from tests/templates/format/test_patch.py
rename to tests/templates/format/test_regex.py
index d8faa68..20e1e61 100644
--- a/tests/templates/format/test_patch.py
+++ b/tests/templates/format/test_regex.py
@@ -1,10 +1,10 @@
import pytest
from collections import OrderedDict
-from calculate.templates.format.patch_format import PatchFormat
+from calculate.templates.format.regex_format import RegexFormat
from calculate.templates.template_engine import ParametersContainer
-@pytest.mark.patch
+@pytest.mark.regex
class TestParsingMethods:
def test_if_input_patch_document_contains_only_regular_expressions_without_any_regex_flags__it_correctly_patches_input_document(self):
input_text = '''
@@ -29,8 +29,8 @@ Another line of endless sadness.
ParameterName = NewValue
'''
- patch_original = PatchFormat(input_text)
- patch_template = PatchFormat(patch_text)
+ patch_original = RegexFormat(input_text)
+ patch_template = RegexFormat(patch_text)
patch_original.join_template(patch_template)
assert patch_original.document_text == output_text
@@ -78,8 +78,8 @@ Another line of endless sadness.
parameters = ParametersContainer({'multiline': True, 'dotall': True})
- patch_original = PatchFormat(input_text, parameters=parameters)
- patch_template = PatchFormat(patch_text, parameters=parameters)
+ patch_original = RegexFormat(input_text, parameters=parameters)
+ patch_template = RegexFormat(patch_text, parameters=parameters)
patch_original.join_template(patch_template)
assert patch_original.document_text == output_text
diff --git a/tests/templates/format/testfiles/a1/dir/file1.txt b/tests/templates/format/testfiles/a1/dir/file1.txt
index 023bd50..8393bcc 100644
--- a/tests/templates/format/testfiles/a1/dir/file1.txt
+++ b/tests/templates/format/testfiles/a1/dir/file1.txt
@@ -3,6 +3,7 @@ CONFIG_BUILDTIME_EXTABLE_SORT=y
#
# General setup
#
+CONFIG_BUILD_SALT=""
CONFIG_HAVE_KERNEL_GZIP=y
CONFIG_HAVE_KERNEL_BZIP2=y
CONFIG_HAVE_KERNEL_XZ=y
diff --git a/tests/templates/format/testfiles/b1/dir/file1.txt b/tests/templates/format/testfiles/b1/dir/file1.txt
index 8393bcc..474f6f7 100644
--- a/tests/templates/format/testfiles/b1/dir/file1.txt
+++ b/tests/templates/format/testfiles/b1/dir/file1.txt
@@ -1,11 +1,17 @@
CONFIG_IRQ_WORK=y
CONFIG_BUILDTIME_EXTABLE_SORT=y
+CONFIG_THREAD_INFO_IN_TASK=y
#
# General setup
#
+CONFIG_INIT_ENV_ARG_LIMIT=16
+# CONFIG_COMPILE_TEST is not set
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_BUILD_SALT=""
CONFIG_HAVE_KERNEL_GZIP=y
CONFIG_HAVE_KERNEL_BZIP2=y
+CONFIG_HAVE_KERNEL_LZMA=y
CONFIG_HAVE_KERNEL_XZ=y
CONFIG_HAVE_KERNEL_LZO=y
CONFIG_HAVE_KERNEL_LZ4=y
diff --git a/tests/templates/format/testfiles/diff_1.patch b/tests/templates/format/testfiles/diff_1.patch
index 14edf64..d505f5e 100644
--- a/tests/templates/format/testfiles/diff_1.patch
+++ b/tests/templates/format/testfiles/diff_1.patch
@@ -1,6 +1,6 @@
diff -urN a/dir/file1.txt b/dir/file1.txt
---- a/dir/file1.txt 2020-01-29 14:38:21.216540100 +0300
-+++ b/dir/file1.txt 2020-01-29 14:40:19.200542500 +0300
+--- a/dir/file1.txt 2020-06-01 11:43:36.661218600 +0300
++++ b/dir/file1.txt 2020-02-06 12:49:10.941240900 +0300
@@ -1,10 +1,17 @@
CONFIG_IRQ_WORK=y
CONFIG_BUILDTIME_EXTABLE_SORT=y
@@ -20,8 +20,8 @@ diff -urN a/dir/file1.txt b/dir/file1.txt
CONFIG_HAVE_KERNEL_LZO=y
CONFIG_HAVE_KERNEL_LZ4=y
diff -urN a/dir/file2.txt b/dir/file2.txt
---- a/dir/file2.txt 2020-01-29 14:35:45.112536800 +0300
-+++ b/dir/file2.txt 2020-01-29 13:47:05.475437800 +0300
+--- a/dir/file2.txt 2020-06-01 11:43:36.693218600 +0300
++++ b/dir/file2.txt 2020-02-06 12:49:10.949240900 +0300
@@ -8,3 +8,9 @@
The old familiar sting
Try to kill it all away
diff --git a/tests/templates/format/testfiles/diff_2.patch b/tests/templates/format/testfiles/diff_2.patch
index eecd2c6..4b85fa4 100644
--- a/tests/templates/format/testfiles/diff_2.patch
+++ b/tests/templates/format/testfiles/diff_2.patch
@@ -1,7 +1,7 @@
-diff -Naru a1/dir/file1.txt b1/dir/file1.txt
---- a1/dir/file1.txt 2020-01-29 16:12:32.179927200 +0300
-+++ b1/dir/file1.txt 2020-01-29 15:54:03.080863300 +0300
-@@ -1,10 +1,17 @@
+diff -Naur a1/dir/file1.txt b1/dir/file1.txt
+--- a1/dir/file1.txt 2020-06-01 12:07:06.845725100 +0300
++++ b1/dir/file1.txt 2020-06-01 12:07:16.085725300 +0300
+@@ -1,11 +1,17 @@
CONFIG_IRQ_WORK=y
CONFIG_BUILDTIME_EXTABLE_SORT=y
+CONFIG_THREAD_INFO_IN_TASK=y
@@ -12,16 +12,16 @@ diff -Naru a1/dir/file1.txt b1/dir/file1.txt
+# CONFIG_COMPILE_TEST is not set
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
-+CONFIG_BUILD_SALT=""
+ CONFIG_BUILD_SALT=""
CONFIG_HAVE_KERNEL_GZIP=y
CONFIG_HAVE_KERNEL_BZIP2=y
+CONFIG_HAVE_KERNEL_LZMA=y
CONFIG_HAVE_KERNEL_XZ=y
CONFIG_HAVE_KERNEL_LZO=y
CONFIG_HAVE_KERNEL_LZ4=y
-diff -Naru a1/dir/file2.txt b1/dir/file2.txt
+diff -Naur a1/dir/file2.txt b1/dir/file2.txt
--- a1/dir/file2.txt 1970-01-01 03:00:00.000000000 +0300
-+++ b1/dir/file2.txt 2020-01-29 13:47:05.475437800 +0300
++++ b1/dir/file2.txt 2020-06-01 11:44:54.825220300 +0300
@@ -0,0 +1,16 @@
+Nine Inch Nails -- Hurt
+
diff --git a/tests/templates/testfiles/test_root/etc/dir/file.conf b/tests/templates/testfiles/test_root/etc/dir/file.conf
deleted file mode 100644
index afd6900..0000000
--- a/tests/templates/testfiles/test_root/etc/dir/file.conf
+++ /dev/null
@@ -1,5 +0,0 @@
-[section one]
- parameter_1 = value
- parameter_2 = value_2
-[section two]
- other_parameter = other_value
diff --git a/tests/templates/testfiles/test_root/var/db/pkg/test-category/test-package-1.0/CONTENTS b/tests/templates/testfiles/test_root/var/db/pkg/test-category/test-package-1.0/CONTENTS
index f3222cc..e69de29 100644
--- a/tests/templates/testfiles/test_root/var/db/pkg/test-category/test-package-1.0/CONTENTS
+++ b/tests/templates/testfiles/test_root/var/db/pkg/test-category/test-package-1.0/CONTENTS
@@ -1,3 +0,0 @@
-dir /etc
-dir /etc/dir
-obj /etc/dir/file.conf 0b87fea7f5b65cac5012baa2bf647e72 1590764349
diff --git a/tests/templates/testfiles/test_root/var/lib/calculate/config-archive/etc/dir/file.conf b/tests/templates/testfiles/test_root/var/lib/calculate/config-archive/etc/dir/file.conf
index afd6900..0bc8d65 100644
--- a/tests/templates/testfiles/test_root/var/lib/calculate/config-archive/etc/dir/file.conf
+++ b/tests/templates/testfiles/test_root/var/lib/calculate/config-archive/etc/dir/file.conf
@@ -1,5 +1,5 @@
[section one]
- parameter_1 = value
- parameter_2 = value_2
-[section two]
- other_parameter = other_value
+ parameter_1 = value_1
+# Source file
+[section_name]
+ rare_parameter = eternal_value