Source code for pyctest.helpers

#!/home/docs/checkouts/readthedocs.org/user_builds/pyctest/conda/latest/bin/python
# MIT License
#
# Copyright (c) 2018, The Regents of the University of California,
# through Lawrence Berkeley National Laboratory (subject to receipt of any
# required approvals from the U.S. Dept. of Energy).  All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#

from __future__ import absolute_import

__author__ = "Jonathan Madsen"
__copyright__ = "Copyright 2018, The Regents of the University of California"
__credits__ = ["Jonathan Madsen"]
__license__ = "MIT"
__version__ = "0.0.12"
__maintainer__ = "Jonathan Madsen"
__email__ = "jonrobm.programming@gmail.com"
__status__ = "Development"
__all__ = ['Conda', 'FindExePath', 'RemovePath', 'Cleanup', 'GetHomePath',
           'GetSystemVersionInfo', 'ArgumentParser']


import os
import re
import sys
import shutil
import socket
import platform
import warnings
import argparse
import textwrap as _textwrap
from . import pyctest
from . import pycmake


#------------------------------------------------------------------------------#
#
[docs]class Conda(object): """Utility class for getting relevant conda environment variables""" def __init__(self): self.prefix = os.environ.get("CONDA_PREFIX") self.environ = os.environ.get("CONDA_ENVIRONMENT") self.python = os.environ.get("CONDA_PYTHON_EXE") self.default_env = os.environ.get("CONDA_DEFAULT_ENV") self.shlvl = os.environ.get("CONDA_SHLVL") def __str__(self): return "{}={};{}={};{}={};{}={};{}={}".format( "CONDA_PREFIX", self.prefix, "CONDA_ENVIRONMENT", self.environ, "CONDA_PYTHON_EXE", self.python, "CONDA_DEFAULT_ENV", self.default_env, "CONDA_SHLVL", self.shlvl)
#------------------------------------------------------------------------------# #
[docs]def FindExePath(name, path=None): """Function for finding the path to an executable""" try: from shutil import which exepath = which(name, path=path) if exepath is not None: return exepath except: pass try: from distutils.spawn import find_executable return find_executable(name, path=path) except: pass return None
#------------------------------------------------------------------------------# #
[docs]def RemovePath(path): """Function for safely removing a folder""" try: import shutil if path is not None and os.path.exists(path): if os.path.isdir(path) and path != os.getcwd(): shutil.rmtree(path) else: os.remove(path) except Exception as e: print("non-fatal exception on '{}': {}".format(path, e))
#------------------------------------------------------------------------------# #
[docs]def Cleanup(path=None, extra=[], exclude=[]): """ This function removes PyCTest files/folders that are copied into or generated in the given path during running PyCTest Parameters ---------- path : str, optional alternative path (default=pyctest.SOURCE_DIRECTORY) extra : list, optional any additional files/folders to remove exclude : list, optional use this to exclude deleting files/folders """ filelist = ["CTestConfig.cmake", "CTestCustom.cmake", "CTestTestfile.cmake", "DartConfiguration.tcl", "Init.cmake", "Stages.cmake", "Utilities.cmake", "Makefile", "Testing", "cmake_install.cmake", "__pycache__", "CMakeFiles", "CMakeCache.txt"] if len(extra) > 0: filelist.extend(extra) for f in exclude: if f in filelist: filelist.remove(f) if path is None: path = pyctest.SOURCE_DIRECTORY for f in filelist: RemovePath(os.path.join(path, f))
#------------------------------------------------------------------------------# #
[docs]def GetHomePath(): """Get the user's home drive for the operating system""" home = os.environ.get("HOME") if home is None: home_d = os.environ.get("HomeDrive") home_p = os.environ.get("HomePath") if home_d is not None and home_p is not None: home = os.path.join(home_d, home_p) return home
#------------------------------------------------------------------------------# #
[docs]def GetSystemVersionInfo(): """Version info for operating system""" version_info = "{}".format(platform.system()) if platform.system() == "Windows": version_info = "Windows {}".format(platform.version()) if platform.system() == 'Darwin': version_info = "macOS {}".format(platform.mac_ver()[0]) elif platform.system() == 'Linux': version_info = "{} {}".format(platform.linux_distribution()[0], platform.linux_distribution()[1]) return version_info
#-------------------------------------------------------------------------# # def _get_verbosity(): '''Determine the verbosity Default: 1 ''' verbose = os.environ.get("PYCTEST_VERBOSE") if verbose is not None: verbose = int(verbose) else: verbose = 1 return verbose #-------------------------------------------------------------------------# # def _get_verbosity_flag(): '''Determine the initial verbosity flag based verbose setting Default: "-V" ''' verbose = _get_verbosity() if verbose < 1: return "" elif verbose == 1: return "-V" elif verbose == 2: return "-VV" elif verbose > 2: return "--debug" #------------------------------------------------------------------------------# # class LineWrapRawTextHelpFormatter(argparse.RawDescriptionHelpFormatter): def __init__(self, prog, indent_increment=2, max_help_position=80, width=160): super(argparse.RawDescriptionHelpFormatter, self).__init__( prog=prog, indent_increment=indent_increment, max_help_position=max_help_position, width=width) def _split_lines(self, text, width): #print("Splitting lines...\n\tTEXT: {}\n\tWIDTH:{}\n".format(text, width)) #text = self._whitespace_matcher.sub(' ', text).strip() return _textwrap.wrap(text, width) #------------------------------------------------------------------------------# #
[docs]class ArgumentParser(argparse.ArgumentParser): """Adds common command-line options for PyCTest Note: Inherit from this class to create custom options Parameters ---------- project_name: str Name of the project source_dir: str relative path to the source code binary_dir: str relative path to the binary (build) directory mode: str='Continuous', optional Workflow setting Options: Start, Update, Configure, Build, Test, Coverage, MemCheck, Submit, Stages stages: list=['Start', 'Update', 'Configure', 'Build', 'Test', 'Coverage', 'MemCheck'] In "Stages" mode, these are the workflow components to execute If a command for the stage is not set, e.g. CTEST_COVERAGE_COMMAND is not set, then the stage is skipped trigger: str='None', optional Deprecated, has no effect model: str='Continuous', optional Submission track. Common choices: Continuous, Nightly, Experimental site: str = platform.node(), optional The ID of the submission site jobs: int = 1, optional Number of tests to execute in parallel submit: bool, optional Enable/disable submission by default vcs_type: str, optional Version control type: bzr, cvs, git, hg, p4, svn checkout_command: str, optional Define the default checkout command update_command: str, optional Define the default update command configure_command: str, optional Define the default configure command build_command: str, optional Define the default configure command coverage_command: str, optional Define the default coverage command memcheck_command: str, optional Define the default memory check command build_choices: str, optional If running CMake, define the build type [Release, RelWithDebInfo, Debug, MinSizeRel] use_launchers: bool, optional For build trees generated by CMake using one of the Makefile Generators or the Ninja generator, specify whether the CTEST_USE_LAUNCHERS feature is enabled by the CTestUseLaunchers module (also included by the CTest module). When enabled, the generated build system wraps each invocation of the compiler, linker, or custom command line with a "launcher" that communicates with CTest via environment variables and files to report granular build warning and error information. Otherwise, CTest must "scrape" the build output log for diagnostics. cleanup_extra: list, optional When `--pyctest-cleanup` or `--pyctest-clean-first` is invoked, these additional files/folders will be removed cleanup_exclude: list, optional When `--pyctest-cleanup` or `--pyctest-clean-first` is invoked, these additional files/folders will be excluded from removal cdash_version: str=None, optional Specify the version of CDash on the server submit_retry_count: int=1, optional Specify a number of attempts to retry submission on network failure submit_retry_delay: int=30, optional Specify a delay before retrying submission on network failure curl_options: list=None, optional Curl options: 'CURLOPT_SSL_VERIFYPEER_OFF' and 'CURLOPT_SSL_VERIFYHOST_OFF' drop_location: str="/submit.php?project={}".format(project_name), optional The path on the dashboard server to send the submission drop_method: str="https", optional Specify the method by which results should be submitted to the dashboard server. The value may be cp, ftp, http, https, scp, or xmlrpc (if CMake was built with support for it) drop_site: str="cdash.nersc.gov", optional The dashboard server name (for ftp, http, and https, scp, and xmlrpc) drop_site_password: str=None, optional The dashboard server login password, if any (for ftp, http, and https). drop_site_user: str=None, optional The dashboard server login user name, if any (for ftp, http, and https). nightly_start_time: str="01:00:00 UTC", optional In the Nightly dashboard mode, specify the "nightly start time". With centralized version control systems (cvs and svn), the Update step checks out the version of the software as of this time so that multiple clients choose a common version to test. This is not well-defined in distributed version-control systems so the setting is ignored. """ #-------------------------------------------------------------------------# # def __init__(self, project_name, source_dir, binary_dir, vcs_type=None, cleanup_extra=[], cleanup_exclude=[], drop_method="https", drop_site="cdash.nersc.gov", drop_location="/submit.php?project=${CTEST_PROJECT_NAME}", drop_site_user=None, drop_site_password=None, use_launchers=False, submit_retry_count=1, submit_retry_delay=30, curl_options=None, cdash_version=None, nightly_start_time="01:00:00 UTC", jobs=1, submit=False, stages=["Start", "Update", "Configure", "Build", "Test", "Coverage", "MemCheck"], trigger="None", site=socket.gethostname().strip('.local'), ctest_args=[_get_verbosity_flag()], mode="Stages", checkout_command=None, update_command=None, configure_command=None, build_command=None, coverage_command=None, memcheck_command=None, python_exe=sys.executable, build_type=None, model="Continuous", build_choices=["Release", "RelWithDebInfo", "Debug", "MinSizeRel"], # standard argparse.ArgumentParser arguments prog=None, usage=None, description="PyCTest argparse. Arguments after first '--' are passed directly " \ "to CTest, arguments after second '--' are passed directly to CMake", epilog=None, parents=[], formatter_class=LineWrapRawTextHelpFormatter, prefix_chars='-', fromfile_prefix_chars=None, argument_default=None, conflict_handler='resolve', add_help=True): # initilize parent super(ArgumentParser, self).__init__(prog=prog, usage=usage, description=description, epilog=epilog, parents=parents, formatter_class=formatter_class, prefix_chars=prefix_chars, fromfile_prefix_chars=fromfile_prefix_chars, argument_default=argument_default, conflict_handler=conflict_handler, add_help=add_help) self.checkout_command = checkout_command self.update_command = checkout_command self.configure_command = configure_command self.build_command = build_command self.coverage_command = coverage_command self.memcheck_command = memcheck_command self.cleanup_extra = cleanup_extra self.cleanup_exclude = cleanup_exclude if checkout_command is not None: pyctest.CHECKOUT_COMMAND = "{}".format(checkout_command) if update_command is not None: pyctest.UPDATE_COMMAND = "{}".format(update_command) if configure_command is not None: pyctest.CONFIGURE_COMMAND = "{}".format(configure_command) if build_command is not None: pyctest.BUILD_COMMAND = "{}".format(build_command) if coverage_command is not None: pyctest.COVERAGE_COMMAND = "{}".format(coverage_command) if memcheck_command is not None: pyctest.MEMORYCHECK_COMMAND = "{}".format(memcheck_command) # limit the mode choices mode_choices = ["Start", "Update", "Configure", "Build", "Test", "Coverage", "MemCheck", "Submit", "Stages"] # stage choices stage_choices = ["Start", "Update", "Configure", "Build", "Test", "Coverage", "MemCheck", "Submit"] # choices for submission model model_choices = ["Nightly", "Continuous", "Experimental"] # choices for submit trigger trigger_choices = ["Build", "Test", "Coverage", "MemCheck", "None"] # version control options vcs_choices = ["bzr", "cvs", "git", "hg", "p4", "svn"] #---------------------------------------------------------------------# self.add_argument("-F", # clean [F]irst "--pyctest-clean-first", help="Remove standard PyCTest files and binary directory (if not source directory)", action="store_true") self.add_argument("-S", # [S]ubmit "--pyctest-submit", help="Enable submission to dashboard", action='store_true', default=submit) self.add_argument("-A", # [A]ppend "--pyctest-append", help="Append to last invocation of CTest", action='store_true') #---------------------------------------------------------------------# self.add_argument("-j", # [J]obs "--pyctest-jobs", help="number of concurrent jobs", type=int, metavar='<INT>', default=jobs) self.add_argument("-m", # [M]ode "--pyctest-mode", help="Run specific stage. Choices: [{}]".format(",".join(mode_choices)), type=str, choices=mode_choices, metavar='<TYPE>', default=mode) self.add_argument("-b", # [B]uild name "--pyctest-build-name", type=str, default=None, metavar='<NAME>', help="Build name for identification") self.add_argument("-i", # source -> [I]nput "--pyctest-source-dir", help="Source directory", type=str, metavar="<PATH>", default=source_dir) self.add_argument("-o", # binary -> [O]utput "--pyctest-binary-dir", help="Binary/build/working directory", type=str, metavar="<PATH>", default=binary_dir) #---------------------------------------------------------------------# self.add_argument("-M", # [M]odel "--pyctest-model", help="CTest submission model (track). Choices: [{}]".format(",".join(model_choices)), type=str, metavar='<TYPE>', choices=model_choices, default=model) self.add_argument("-H", # [H]ost "--pyctest-site", help="CTest submission site", type=str, metavar='<SITE>', default=site) self.add_argument("-P", # [P]ython exe "--pyctest-python-exe", help="Python executable to use. This can be absolue, relative, or CMake path", type=str, metavar='<EXE>', default=python_exe) self.add_argument("-N", # [N]ame "--pyctest-project-name", help="Name of project using PyCTest", type=str, metavar='<NAME>', default=project_name) #---------------------------------------------------------------------# self.add_argument("-C", # [C]lean "--pyctest-cleanup", help="Remove standard PyCTest files and binary directory (if not source directory) and exit", nargs='+', metavar='<PATH>', type=str) self.add_argument("-L", # [L]abels "--pyctest-subproject-labels", help="Add labels for subproject", metavar='<LABEL>', nargs='+', type=str) self.add_argument("-T", # [T]est-action "--pyctest-stages", help="Run multiple stages. Choices: [{}]".format(",".join(stage_choices)), type=str, choices=stage_choices, default=stages, metavar="<TYPE>", nargs='*') #---------------------------------------------------------------------# self.add_argument("-fc", # [F]ortran compiler "--pyctest-fortran-compiler", help="Specify Fortan compiler (if needed)", default=os.environ.get("FC"), metavar='<EXE>', type=str) self.add_argument("-cc", # [CC] compiler "--pyctest-c-compiler", help="Specify the C compiler (if needed)", default=os.environ.get("CC"), metavar='<EXE>', type=str) self.add_argument("-cxx", # [CXX] compiler "--pyctest-cxx-compiler", help="Specify C++ compiler (if needed)", default=os.environ.get("CXX"), metavar='<EXE>', type=str) #---------------------------------------------------------------------# self.add_argument("--pyctest-token", help="CTest site token for submission", metavar='<TOKEN>', type=str) self.add_argument("--pyctest-token-file", help="Path to file for CTest site token for submission", metavar='<FILE>', type=str) self.add_argument("--pyctest-vcs-type", help="""Set to enable revision ID discovery during the Update stage. Choices: [{}]""".format(",".join(vcs_choices)), default=vcs_type, type=str, metavar="<TYPE>", choices=vcs_choices) self.add_argument("--pyctest-build-type", help="""Specify CMAKE_BUILD_TYPE (if using CMake). Choices: [{}]""".format(",".join(build_choices)), default=build_type, type=str, metavar='<TYPE>', choices=build_choices) self.add_argument("--pyctest-trigger", help="DEPRECATED", metavar='<TYPE>', type=str, choices=trigger_choices, default=trigger) self.add_argument("--pyctest-use-launchers", default=use_launchers, metavar='<BOOL>', help="Enable launchers") self.add_argument("--pyctest-ctest-args", help="CTest arguments", type=str, default=ctest_args, metavar='<ARG>', nargs='*') #---------------------------------------------------------------------# def _add_argument(option_name, option_type, option_default): ctest_var = "CTEST_" + option_name.upper() web = "https://cmake.org/cmake/help/latest/variable/" web = "{0}/{1}.html#variable:{1}".format(web, ctest_var) option_name = option_name.lower().replace('_', '-') self.add_argument("--pyctest-{}".format(option_name.lower()), help="Set CTest variable: '{}'".format(ctest_var), type=option_type, metavar="<{}>".format(option_type.__name__.upper()), #metavar="<{}>".format(option_name.upper().replace('-', '_')), default=option_default) _add_argument("cdash_version", str, cdash_version) _add_argument("submit_retry_count", int, submit_retry_count) _add_argument("submit_retry_delay", int, submit_retry_delay) _add_argument("curl_options", list, curl_options) _add_argument("drop_location", str, drop_location) _add_argument("drop_method", str, drop_method) _add_argument("drop_site", str, drop_site) _add_argument("drop_site_password", str, drop_site_password) _add_argument("drop_site_user", str, drop_site_user) _add_argument("nightly_start_time", str, nightly_start_time) self.add_argument("--pyctest-update-version-only", help="""Specify that you want the version control update command to only discover the current version that is checked out, and not to update to a different version""", action='store_true') #-------------------------------------------------------------------------# #
[docs] def process_args(self, args): """Process PyCTest args""" binary_dir = os.path.realpath(args.pyctest_binary_dir) source_dir = os.path.realpath(args.pyctest_source_dir) # cleanup if args.pyctest_cleanup: for dir in args.pyctest_cleanup: Cleanup(dir, self.cleanup_extra, self.cleanup_exclude) if source_dir != binary_dir: RemovePath(binary_dir) sys.exit(0) if args.pyctest_c_compiler is not None: os.environ["CC"] = "{}".format(args.pyctest_c_compiler) if args.pyctest_cxx_compiler is not None: os.environ["CXX"] = "{}".format(args.pyctest_cxx_compiler) if args.pyctest_fortran_compiler is not None: os.environ["FC"] = "{}".format(args.pyctest_fortran_compiler) # clean if args.pyctest_clean_first: Cleanup(binary_dir, self.cleanup_extra, self.cleanup_exclude) if source_dir != binary_dir: RemovePath(binary_dir) # make binary directory if not os.path.exists(binary_dir): os.makedirs(binary_dir) if args.pyctest_mode != "Stages": args.pyctest_stages = [args.pyctest_mode] # if submit enabled but was not included in stages if args.pyctest_submit and not "Submit" in args.pyctest_stages: args.pyctest_stages.append("Submit") # the name of the project pyctest.PROJECT_NAME = args.pyctest_project_name # source directory pyctest.SOURCE_DIRECTORY = source_dir # binary directory pyctest.BINARY_DIRECTORY = binary_dir # site of CDash submission pyctest.SITE = args.pyctest_site if (args.pyctest_site == platform.node() and os.environ.get("CTEST_SITE") is not None): pyctest.SITE = os.environ.get("CTEST_SITE") # build type if args.pyctest_build_type is not None: pyctest.set("BUILD_TYPE", "{}".format(args.pyctest_build_type)) pyctest.set("CTEST_CONFIGURATION_TYPE", "{}".format(args.pyctest_build_type)) # build name for CDash if args.pyctest_build_name is None: pyctest.BUILD_NAME = "[{} {} {}] [Python {}]".format( platform.uname()[0], GetSystemVersionInfo(), platform.uname()[4], platform.python_version()) else: pyctest.BUILD_NAME = "{}".format(args.pyctest_build_name) # the Python to use with scripts pyctest.PYTHON_EXECUTABLE = args.pyctest_python_exe # submit after "Test" has been called pyctest.TRIGGER = args.pyctest_trigger # how to checkout the code pyctest.CHECKOUT_COMMAND = "" # the submission model in CDash (default = Nightly) pyctest.MODEL = args.pyctest_model # update if args.pyctest_vcs_type is not None: pyctest.set("CTEST_UPDATE_TYPE", "{}".format(args.pyctest_vcs_type)) # if specified to not remove an revision if not args.pyctest_update_version_only: pyctest.set("CTEST_UPDATE_VERSION_ONLY", "ON") # custom variables pyctest.CUSTOM_COVERAGE_EXCLUDE = "NONE" pyctest.CUSTOM_MAXIMUM_NUMBER_OF_ERRORS = "200" pyctest.CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS = "300" pyctest.CUSTOM_MAXIMUM_PASSED_TEST_OUTPUT_SIZE = "104857600" if args.pyctest_token: pyctest.set("CTEST_TOKEN", args.pyctest_token) elif args.pyctest_token_file: pyctest.set("CTEST_TOKEN_FILE", args.pyctest_token_file) else: home = GetHomePath() if home is not None: relpath = os.path.join(".tokens", pyctest.PROJECT_NAME) token_path = os.path.join(home, relpath) if os.path.exists(token_path): print("Setting CTEST_TOKEN_FILE to '{}'...".format(token_path)) pyctest.set("CTEST_TOKEN_FILE", token_path) if os.environ.get("CTEST_TOKEN") is not None: pyctest.set("CTEST_TOKEN", os.environ.get("CTEST_TOKEN")) # append the stages to the CTest args args.pyctest_ctest_args.append( "-DSTAGES={}".format(";".join(args.pyctest_stages))) # instruct to execute Stages.cmake args.pyctest_ctest_args.extend(["-S", "Stages.cmake"]) # add the number of jobs if args.pyctest_jobs > 0: args.pyctest_ctest_args.append("-j{}".format(args.pyctest_jobs)) # enable launchers if args.pyctest_use_launchers: pyctest.set("CTEST_USE_LAUNCHERS", "ON") # add subproject labels if args.pyctest_subproject_labels: pyctest.set("CTEST_LABELS_FOR_SUBPROJECTS", ";".join(args.pyctest_subproject_labels)) # if the arguments were provided if len(args.pyctest_ctest_args) > 0: for _arg in args.pyctest_ctest_args: pyctest.ARGUMENTS.extend(_arg.split(" ")) # if append if args.pyctest_append: pyctest.set("CTEST_APPEND", "ON") def _process_argument(var, val): if val is not None: if isinstance(val, list): val = ";".join(val) pyctest.set("CTEST_{}".format(var.upper()), "{}".format(val)) _process_argument("cdash_version", args.pyctest_cdash_version) _process_argument("submit_retry_count", args.pyctest_submit_retry_count) _process_argument("submit_retry_delay", args.pyctest_submit_retry_delay) _process_argument("curl_options", args.pyctest_curl_options) _process_argument("drop_location", args.pyctest_drop_location) _process_argument("drop_method", args.pyctest_drop_method) _process_argument("drop_site", args.pyctest_drop_site) _process_argument("drop_site_password", args.pyctest_drop_site_password) _process_argument("drop_site_user", args.pyctest_drop_site_user) _process_argument("nightly_start_time", args.pyctest_nightly_start_time) print("CTest arguments (default): '{}'".format(" ".join(pyctest.ARGUMENTS)))
#-------------------------------------------------------------------------#
[docs] def parse_args(self, *args, **kwargs): """Parse the arguments Note: Arguments following first double-dash ("--") are passed directly to `ctest` Note: Arguments following second double-dash ("--") are passed directly to `cmake` """ _argv = [] _ctest = [] _cmake = [] _argsets = [_argv, _ctest, _cmake] _i = 0 _separator = '--' for _arg in sys.argv[1:]: if _arg == _separator: _i += 1 if _i >= len(_argsets): sys.exit( "ERROR: Too many \"{}\" separators provided " "(expected at most {}).".format(_separator, len(_argsets) - 1)) else: _argsets[_i].append(_arg) try: sys.argv[1:] = _argv pyctest.ARGUMENTS.extend(_ctest) pycmake.ARGUMENTS.extend(_cmake) print("PyCTest args: {}".format(_argv)) print(" CTest args: {}".format(pyctest.ARGUMENTS + _ctest)) print(" CMake args: {}".format(pycmake.ARGUMENTS + _cmake)) except Exception as e: msg = "\nCommand line argument error:\n\t{}\n".format(e) warnings.warn(msg) # read arguments (fail if unknown option) pargs = super(ArgumentParser, self).parse_args(*args, **kwargs) # process PyCTest args self.process_args(pargs) # return all args return pargs