From: Aaron Conole <aconole@redhat.com>
To: ohilyard@iol.unh.edu
Cc: ci@dpdk.org
Subject: Re: [dpdk-ci] [PATCH] patch parsing: Added library for parsing patch files
Date: Thu, 15 Apr 2021 17:37:19 -0400 [thread overview]
Message-ID: <f7t7dl36xgw.fsf@dhcp-25.97.bos.redhat.com> (raw)
In-Reply-To: <20210415211112.77161-1-ohilyard@iol.unh.edu> (ohilyard@iol.unh.edu's message of "Thu, 15 Apr 2021 17:11:12 -0400")
ohilyard@iol.unh.edu writes:
> From: Owen Hilyard <ohilyard@iol.unh.edu>
>
> Due to the number of edge cases, there were difficulties doing the parsing using pattern matching, so a dedicated library, whatthepatch, is now used for parsing the patches to ensure correctness.
>
> Also added the default behavior that if no tags are given, create_new_execution_file_from_tags.py will act as though all possible tags were given. This is to allow patches where no tags could be found to still have test coverage.
> ---
Missing sign-off.
Also, please reformat the commit message, preferring 75 column width.
Unlike code, commit logs are very sensitive when viewing history, etc.
> config/tests_for_tag.cfg | 30 +++++
> requirements.txt | 1 +
> tools/create_new_execution_file_from_tags.py | 114 +++++++++++++++++++
> tools/patch_parser.py | 85 ++++++++++----
> 4 files changed, 205 insertions(+), 25 deletions(-)
> create mode 100644 config/tests_for_tag.cfg
> create mode 100644 requirements.txt
> create mode 100755 tools/create_new_execution_file_from_tags.py
>
> diff --git a/config/tests_for_tag.cfg b/config/tests_for_tag.cfg
> new file mode 100644
> index 0000000..7d95c4a
> --- /dev/null
> +++ b/config/tests_for_tag.cfg
> @@ -0,0 +1,30 @@
> +[functional]
> +core = dynamic_config,
> + link_status_interrupt,
> + mac_filter,
> + mtu_update,
> + scatter,
> + stats_checks,
> + unit_tests_mbuf
> +driver = dynamic_config,
> + link_status_interrupt,
> + mac_filter,
> + mtu_update,
> + scatter,
> + stats_checks,
> + unit_tests_mbuf
> +application = dynamic_config,
> + link_status_interrupt,
> + mac_filter,
> + mtu_update,
> + scatter,
> + stats_checks,
> + unit_tests_mbuf
> +; Nothing in documentation
> +documentation =
> +
> +[performance]
> +core = nic_single_core_perf
> +driver = nic_single_core_perf
> +application = nic_single_core_perf
> +documentation =
> diff --git a/requirements.txt b/requirements.txt
> new file mode 100644
> index 0000000..f20067d
> --- /dev/null
> +++ b/requirements.txt
> @@ -0,0 +1 @@
> +whatthepatch==1.0.2
> \ No newline at end of file
> diff --git a/tools/create_new_execution_file_from_tags.py b/tools/create_new_execution_file_from_tags.py
> new file mode 100755
> index 0000000..d1d4447
> --- /dev/null
> +++ b/tools/create_new_execution_file_from_tags.py
> @@ -0,0 +1,114 @@
> +#!/usr/bin/env python3
> +
> +# BSD LICENSE
> +#
> +# Copyright(c) 2020 Intel Corporation. All rights reserved.
> +# Copyright © 2018[, 2019] The University of New Hampshire. All rights reserved.
> +# All rights reserved.
> +#
> +# Redistribution and use in source and binary forms, with or without
> +# modification, are permitted provided that the following conditions
> +# are met:
> +#
> +# * Redistributions of source code must retain the above copyright
> +# notice, this list of conditions and the following disclaimer.
> +# * Redistributions in binary form must reproduce the above copyright
> +# notice, this list of conditions and the following disclaimer in
> +# the documentation and/or other materials provided with the
> +# distribution.
> +# * Neither the name of Intel Corporation nor the names of its
> +# contributors may be used to endorse or promote products derived
> +# from this software without specific prior written permission.
> +#
> +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> +import sys
> +from enum import Enum
> +
> +import itertools
> +from configparser import ConfigParser
> +from typing import List, Dict, Set
> +import argparse
> +
> +
> +def parse_comma_delimited_list_from_string(mod_str: str) -> List[str]:
> + return list(map(str.strip, mod_str.split(',')))
> +
> +
> +def map_tags_to_tests(tags: List[str], test_map: Dict[str, List[str]]) -> Set[str]:
> + """
> + Returns a list that is the union of all of the map lookups.
> + """
> + try:
> + return set(
> + filter(lambda test: test != '', set(itertools.chain.from_iterable(map(lambda tag: test_map[tag], tags)))))
> + except KeyError as e:
> + print(f'Tag {e} is not present in tests_for_tag.cfg')
> + exit(1)
> +
> +
> +class TestingType(Enum):
> + functional = 'functional'
> + performance = 'performance'
> +
> + def __str__(self):
> + return self.value
> +
> +
> +if __name__ == '__main__':
> + parser = argparse.ArgumentParser(
> + description='Take a template execution file and add the relevant tests'
> + ' for the given tags to it, creating a new file.')
> + parser.add_argument('config_file_path', help='The path to tests_for_tag.cfg', default='config/tests_for_tag.cfg')
> + parser.add_argument('template_execution_file', help='The path to the execution file to use as a template')
> + parser.add_argument('output_path', help='The path to the output execution file')
> + parser.add_argument('testing_type', type=TestingType, choices=list(TestingType),
> + help='What type of testing to create an execution file for')
> + parser.add_argument('tags', metavar='tag', type=str, nargs='*', help='The tags to create an execution file for.')
> +
> + args = parser.parse_args()
> +
> + tag_to_test_map_parser = ConfigParser()
> + tag_to_test_map_parser.read(args.config_file_path)
> +
> + template_execution_file_parser = ConfigParser()
> + template_execution_file_parser.read(args.template_execution_file)
> +
> + test_map = {key: parse_comma_delimited_list_from_string(value.strip()) for key, value in
> + tag_to_test_map_parser[str(args.testing_type)].items()}
> +
> + tests = map_tags_to_tests(args.tags, test_map)
> +
> + try:
> + output_file = open(args.output_path, 'x')
> + except FileExistsError:
> + output_file = open(args.output_path, 'w')
> +
> + for execution_plan in template_execution_file_parser:
> + # The DEFAULT section is always present and contains top-level items, so it needs to be ignored
> + if execution_plan != 'DEFAULT':
> + test_allowlist = parse_comma_delimited_list_from_string(
> + template_execution_file_parser[execution_plan]['test_suites'])
> + tests_to_run = list(set(test_allowlist).intersection(tests))
> + tests_to_run.sort()
> + template_execution_file_parser[execution_plan]['test_suites'] = ", ".join(tests_to_run)
> +
> + if args.testing_type == TestingType.functional:
> + template_execution_file_parser[execution_plan]['parameters'] += ':func=true:perf=false'
> + elif args.testing_type == TestingType.performance:
> + template_execution_file_parser[execution_plan]['parameters'] += ':func=false:perf=true'
> + else:
> + # This code should be unreachable, since this is checked at the top of the file
> + print("Fatal error: testing type is neither performance nor functional", file=sys.stderr)
> + exit(1)
> +
> + template_execution_file_parser.write(output_file)
> diff --git a/tools/patch_parser.py b/tools/patch_parser.py
> index 01fc55d..cc91cd2 100755
> --- a/tools/patch_parser.py
> +++ b/tools/patch_parser.py
> @@ -1,27 +1,58 @@
> #!/usr/bin/env python3
>
> -import itertools
> -import sys
> +import argparse
> from configparser import ConfigParser
> from typing import List, Dict, Set
>
> +import itertools
> +# BSD LICENSE
> +#
> +# Copyright(c) 2020 Intel Corporation. All rights reserved.
> +# Copyright © 2018[, 2019] The University of New Hampshire. All rights reserved.
> +# All rights reserved.
> +#
> +# Redistribution and use in source and binary forms, with or without
> +# modification, are permitted provided that the following conditions
> +# are met:
> +#
> +# * Redistributions of source code must retain the above copyright
> +# notice, this list of conditions and the following disclaimer.
> +# * Redistributions in binary form must reproduce the above copyright
> +# notice, this list of conditions and the following disclaimer in
> +# the documentation and/or other materials provided with the
> +# distribution.
> +# * Neither the name of Intel Corporation nor the names of its
> +# contributors may be used to endorse or promote products derived
> +# from this software without specific prior written permission.
> +#
> +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> +import sys
>
> -def get_patch_files(patch_file: str) -> List[str]:
> +try:
> + import whatthepatch
> +except ImportError:
> + print("Please install whatthepatch, a patch parsing library", file=sys.stderr)
> + exit(1)
> +
> +
> +def get_changed_files_in_patch(patch_file: str) -> List[str]:
> with open(patch_file, 'r') as f:
> - lines = list(itertools.takewhile(
> - lambda line: line.strip().endswith('+') or line.strip().endswith('-'),
> - itertools.dropwhile(
> - lambda line: not line.strip().startswith("---"),
> - f.readlines()
> - )
> - ))
> - filenames = map(lambda line: line.strip().split(' ')[0], lines)
> - # takewhile includes the --- which starts the filenames
> - return list(filenames)[1:]
> + filenames = map(lambda diff: diff.header.new_path, whatthepatch.parse_patch(f.read()))
> + return list(filenames)
>
>
> def get_all_files_from_patches(patch_files: List[str]) -> Set[str]:
> - return set(itertools.chain.from_iterable(map(get_patch_files, patch_files)))
> + return set(itertools.chain.from_iterable(map(get_changed_files_in_patch, patch_files)))
>
>
> def parse_comma_delimited_list_from_string(mod_str: str) -> List[str]:
> @@ -47,18 +78,22 @@ def get_tags_for_patches(patch_files: Set[str], dir_attrs: Dict[str, Set[str]])
> ))
>
>
> -if len(sys.argv) < 3:
> - print("usage: patch_parser.py <path to patch_parser.cfg> <patch file>...")
> - exit(1)
> +if __name__ == '__main__':
> + parser = argparse.ArgumentParser(
> + description='Takes a patch file and a config file and creates a list of tags for that patch')
> + parser.add_argument('config_file_path', help='The path to patch_parser.cfg', default='config/patch_parser.cfg')
> + parser.add_argument('patch_file_paths', help='A list of patch files', type=str, metavar='patch file', nargs='+')
> +
> + args = parser.parse_args()
>
> -conf_obj = ConfigParser()
> -conf_obj.read(sys.argv[1])
> + conf_obj = ConfigParser()
> + conf_obj.read(args.config_file_path)
>
> -patch_files = get_all_files_from_patches(sys.argv[2:])
> -dir_attrs = get_dictionary_attributes_from_config_file(conf_obj)
> -priority_list = parse_comma_delimited_list_from_string(conf_obj['Priority']['priority_list'])
> + patch_files = get_all_files_from_patches(args.patch_file_paths)
> + dir_attrs = get_dictionary_attributes_from_config_file(conf_obj)
> + priority_list = parse_comma_delimited_list_from_string(conf_obj['Priority']['priority_list'])
>
> -unordered_tags: Set[str] = get_tags_for_patches(patch_files, dir_attrs)
> -ordered_tags: List[str] = [tag for tag in priority_list if tag in unordered_tags]
> + unordered_tags: Set[str] = get_tags_for_patches(patch_files, dir_attrs)
> + ordered_tags: List[str] = [tag for tag in priority_list if tag in unordered_tags]
>
> -print("\n".join(ordered_tags))
> + print("\n".join(ordered_tags))
prev parent reply other threads:[~2021-04-15 21:37 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-12-04 19:45 [dpdk-ci] [PATCH] ci: added patch parser for " Owen Hilyard
2021-01-14 16:53 ` Owen Hilyard
2021-01-14 17:59 ` [dpdk-ci] [PATCH V2] patch-tagging: Added tool to convert tags into dts execution file ohilyard
2021-01-14 18:19 ` Owen Hilyard
2021-04-15 20:42 ` Aaron Conole
2021-04-15 21:14 ` Owen Hilyard
2021-04-15 20:38 ` [dpdk-ci] [PATCH] ci: added patch parser for patch files Aaron Conole
2021-04-15 21:11 ` [dpdk-ci] [PATCH] patch parsing: Added library for parsing " ohilyard
2021-04-15 21:37 ` Aaron Conole [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f7t7dl36xgw.fsf@dhcp-25.97.bos.redhat.com \
--to=aconole@redhat.com \
--cc=ci@dpdk.org \
--cc=ohilyard@iol.unh.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).