chore: import upstream snapshot with attribution
tools_continuous_delivery / Private PyPI non-main branch release (push) Has been skipped
tools_continuous_delivery / Private PyPI main branch release (push) Failing after 2m42s
Publish Promptflow Doc / Build (push) Has been cancelled
Publish Promptflow Doc / Deploy (push) Has been cancelled
Flake8 Lint / flake8 (push) Has been cancelled
Spell check CI / Spell_Check (push) Has been cancelled
tools_continuous_delivery / Private PyPI non-main branch release (push) Has been skipped
tools_continuous_delivery / Private PyPI main branch release (push) Failing after 2m42s
Publish Promptflow Doc / Build (push) Has been cancelled
Publish Promptflow Doc / Deploy (push) Has been cancelled
Flake8 Lint / flake8 (push) Has been cancelled
Spell check CI / Spell_Check (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from azure.keyvault.secrets import SecretClient
|
||||
from azure.identity import ClientSecretCredential, DefaultAzureCredential
|
||||
|
||||
|
||||
CONNECTION_FILE_NAME = "connections.json"
|
||||
CONNECTION_TPL_FILE_PATH = Path(".") / "src/promptflow" / "dev-connections.json.example"
|
||||
|
||||
|
||||
def get_secret_client(
|
||||
tenant_id: str, client_id: str, client_secret: str
|
||||
) -> SecretClient:
|
||||
try:
|
||||
if (tenant_id is None) or (client_id is None) or (client_secret is None):
|
||||
credential = DefaultAzureCredential()
|
||||
client = SecretClient(
|
||||
vault_url="https://promptflowprod.vault.azure.net/",
|
||||
credential=credential,
|
||||
)
|
||||
else:
|
||||
credential = ClientSecretCredential(tenant_id, client_id, client_secret)
|
||||
client = SecretClient(
|
||||
vault_url="https://github-promptflow.vault.azure.net/",
|
||||
credential=credential,
|
||||
)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
return client
|
||||
|
||||
|
||||
def get_secret(secret_name: str, client: SecretClient):
|
||||
secret = client.get_secret(secret_name)
|
||||
|
||||
return secret.value
|
||||
|
||||
|
||||
def list_secret_names(client: SecretClient) -> list:
|
||||
secret_properties = client.list_properties_of_secrets()
|
||||
|
||||
return [secret.name for secret in secret_properties]
|
||||
|
||||
|
||||
def fill_key_to_dict(template_dict, keys_dict):
|
||||
if not isinstance(template_dict, dict):
|
||||
return
|
||||
for key, val in template_dict.items():
|
||||
if isinstance(val, str) and val in keys_dict:
|
||||
template_dict[key] = keys_dict[val]
|
||||
continue
|
||||
fill_key_to_dict(val, keys_dict)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--tenant_id", type=str, help="The tenant id of the service principal"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--client_id", type=str, help="The client id of the service principal"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--client_secret", type=str, help="The client secret of the service principal"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--target_folder", type=str, help="The target folder to save the generated file"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
template_dict = json.loads(
|
||||
open(CONNECTION_TPL_FILE_PATH.resolve().absolute(), "r").read()
|
||||
)
|
||||
file_path = (
|
||||
(Path(".") / args.target_folder / CONNECTION_FILE_NAME)
|
||||
.resolve()
|
||||
.absolute()
|
||||
.as_posix()
|
||||
)
|
||||
print(f"file_path: {file_path}")
|
||||
|
||||
client = get_secret_client(
|
||||
tenant_id=args.tenant_id,
|
||||
client_id=args.client_id,
|
||||
client_secret=args.client_secret,
|
||||
)
|
||||
all_secret_names = list_secret_names(client)
|
||||
data = {
|
||||
secret_name: get_secret(secret_name, client) for secret_name in all_secret_names
|
||||
}
|
||||
|
||||
fill_key_to_dict(template_dict, data)
|
||||
|
||||
with open(file_path, "w") as f:
|
||||
json.dump(template_dict, f)
|
||||
@@ -0,0 +1,17 @@
|
||||
name: release-env
|
||||
channels:
|
||||
- defaults
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- pip
|
||||
- pip:
|
||||
- setuptools
|
||||
- twine==4.0.0
|
||||
- portalocker~=1.2
|
||||
- setuptools_rust
|
||||
- pytest
|
||||
- pytest-xdist
|
||||
- pytest-sugar
|
||||
- pytest-timeout
|
||||
- azure-keyvault
|
||||
- azure-identity
|
||||
@@ -0,0 +1,128 @@
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from utils import Color, run_command, print_red
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=Color.RED + "Test Coverage for Promptflow!" + Color.END + "\n")
|
||||
|
||||
parser.add_argument("-p", required=True, nargs="+", help="The paths to calculate code coverage")
|
||||
parser.add_argument("-t", required=True, nargs="+", help="The path to the tests")
|
||||
parser.add_argument("-l", required=True, help="Location to run tests in")
|
||||
parser.add_argument(
|
||||
"-m",
|
||||
required=True,
|
||||
help="Pytest marker to identify the tests to run",
|
||||
default="all",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
required=False,
|
||||
help="Pytest output file name",
|
||||
default="test-results.xml",
|
||||
)
|
||||
parser.add_argument("-n", help="Pytest number of process to run the tests", default="auto")
|
||||
parser.add_argument(
|
||||
"--model-name",
|
||||
help="The model file name to run the tests",
|
||||
type=str,
|
||||
default="",
|
||||
)
|
||||
parser.add_argument("--timeout", help="Timeout for individual tests (seconds)", type=str, default="")
|
||||
parser.add_argument(
|
||||
"--coverage-config",
|
||||
help="The path of code coverage config file",
|
||||
type=str,
|
||||
default="",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--disable-cov-branch",
|
||||
action="store_true",
|
||||
help="Whether to enable branch coverage calculation",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--ignore-glob",
|
||||
help="The path of ignored test file",
|
||||
type=str,
|
||||
default="",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
print("Working directory: " + str(os.getcwd()))
|
||||
print("Args.p: " + str(args.p))
|
||||
print("Args.t: " + str(args.t))
|
||||
print("Args.l: " + str(args.l))
|
||||
print("Args.m: " + str(args.m))
|
||||
print("Args.n: " + str(args.n))
|
||||
print("Args.o: " + str(args.o))
|
||||
print("Args.model-name: " + str(args.model_name))
|
||||
print("Args.timeout: " + str(args.timeout))
|
||||
print("Args.coverage-config: " + str(args.coverage_config))
|
||||
print("Args.ignore-glob: " + str(args.ignore_glob))
|
||||
print("Args.disable-cov-branch: " + str(args.disable_cov_branch))
|
||||
|
||||
test_paths_list = [str(Path(path).absolute()) for path in args.t]
|
||||
|
||||
# display a list of all Python packages installed in the current Python environment
|
||||
run_command(["pip", "list"])
|
||||
run_command(["pip", "show", "promptflow", "promptflow-sdk"])
|
||||
|
||||
pytest_command = ["pytest", f"--junitxml={args.o}"]
|
||||
pytest_command += test_paths_list
|
||||
if args.coverage_config:
|
||||
if args.p:
|
||||
cov_path_list = [f"--cov={path}" for path in args.p]
|
||||
pytest_command += cov_path_list
|
||||
if not args.disable_cov_branch:
|
||||
pytest_command += ["--cov-branch"]
|
||||
pytest_command += [ # noqa: W503
|
||||
"--cov-report=term",
|
||||
"--cov-report=html",
|
||||
"--cov-report=xml",
|
||||
]
|
||||
pytest_command = pytest_command + [f"--cov-config={args.coverage_config}"]
|
||||
if args.ignore_glob:
|
||||
pytest_command = pytest_command + [f"--ignore-glob={args.ignore_glob}"]
|
||||
pytest_command += [
|
||||
"-n",
|
||||
args.n,
|
||||
"--dist",
|
||||
"loadfile",
|
||||
"--log-level=info",
|
||||
"--log-format=%(asctime)s %(levelname)s %(message)s",
|
||||
"--log-date-format=[%Y-%m-%d %H:%M:%S]",
|
||||
"--durations=5",
|
||||
"-ra",
|
||||
"-vv",
|
||||
]
|
||||
|
||||
if args.timeout:
|
||||
pytest_command = pytest_command + [
|
||||
"--timeout",
|
||||
args.timeout,
|
||||
"--timeout_method",
|
||||
"thread",
|
||||
]
|
||||
|
||||
if args.m != "all":
|
||||
pytest_command = pytest_command + ["-m", args.m]
|
||||
|
||||
if args.model_name:
|
||||
pytest_command = pytest_command + ["--model-name", args.model_name]
|
||||
|
||||
# pytest --junit-xml=test-results.xml --cov=azure.ai.ml --cov-report=html --cov-report=xml -ra ./tests/*/unittests/
|
||||
error_code, _ = run_command(pytest_command, throw_on_retcode=False)
|
||||
# https://docs.pytest.org/en/7.1.x/reference/exit-codes.html
|
||||
if error_code == 1:
|
||||
print_red("Tests were collected and run but some of the tests failed.")
|
||||
elif error_code == 2:
|
||||
print_red("Test execution was interrupted by the user.")
|
||||
elif error_code == 3:
|
||||
print_red("Internal error happened while executing tests.")
|
||||
elif error_code == 4:
|
||||
print_red("pytest command line usage error.")
|
||||
elif error_code == 5:
|
||||
print_red("No tests were collected.")
|
||||
sys.exit(error_code)
|
||||
@@ -0,0 +1,112 @@
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
module_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Color:
|
||||
PURPLE = "\033[95m"
|
||||
CYAN = "\033[96m"
|
||||
DARKCYAN = "\033[36m"
|
||||
BLUE = "\033[94m"
|
||||
GREEN = "\033[92m"
|
||||
YELLOW = "\033[93m"
|
||||
RED = "\033[91m"
|
||||
BOLD = "\033[1m"
|
||||
UNDERLINE = "\033[4m"
|
||||
END = "\033[0m"
|
||||
|
||||
|
||||
def print_red(message):
|
||||
print(Color.RED + message + Color.END)
|
||||
|
||||
|
||||
def print_blue(message):
|
||||
print(Color.BLUE + message + Color.END)
|
||||
|
||||
|
||||
def get_test_files(testpath):
|
||||
if os.path.isfile(testpath):
|
||||
return [testpath]
|
||||
else:
|
||||
res = []
|
||||
for root, dirs, files in os.walk(testpath):
|
||||
module_logger.debug("Searching %s for files ending in 'tests.py'", root)
|
||||
res.extend([os.path.join(root, file) for file in files if file.endswith("tests.py")])
|
||||
return res
|
||||
|
||||
|
||||
def retry(fn, num_attempts=3):
|
||||
if num_attempts <= 0:
|
||||
raise Exception("Illegal num_attempts: {}".format(num_attempts))
|
||||
count = 0
|
||||
for _ in range(0, num_attempts):
|
||||
try:
|
||||
return fn()
|
||||
except Exception:
|
||||
count += 1
|
||||
print("Execution failed on attempt {} out of {}".format(count, num_attempts))
|
||||
print("Exception trace:")
|
||||
traceback.print_exc()
|
||||
if count == num_attempts:
|
||||
print("Execution failed after {} attempts".format(count))
|
||||
raise
|
||||
|
||||
|
||||
def _run_command(
|
||||
commands,
|
||||
cwd=None,
|
||||
stderr=subprocess.STDOUT,
|
||||
shell=False,
|
||||
env=None,
|
||||
stream_stdout=True,
|
||||
throw_on_retcode=True,
|
||||
logger=None,
|
||||
):
|
||||
if logger is None:
|
||||
logger = module_logger
|
||||
|
||||
if cwd is None:
|
||||
cwd = os.getcwd()
|
||||
|
||||
t0 = time.perf_counter()
|
||||
try:
|
||||
logger.debug("[RunCommand]Executing {0} in {1}".format(commands, cwd))
|
||||
out = ""
|
||||
p = subprocess.Popen(commands, stdout=subprocess.PIPE, stderr=stderr, cwd=cwd, shell=shell, env=env)
|
||||
for line in p.stdout:
|
||||
line = line.decode("utf-8").rstrip()
|
||||
if line and line.strip():
|
||||
logger.debug(line)
|
||||
if stream_stdout:
|
||||
sys.stdout.write(line)
|
||||
sys.stdout.write("\n")
|
||||
out += line
|
||||
out += "\n"
|
||||
p.communicate()
|
||||
retcode = p.poll()
|
||||
if throw_on_retcode:
|
||||
if retcode:
|
||||
raise subprocess.CalledProcessError(retcode, p.args, output=out, stderr=p.stderr)
|
||||
return retcode, out
|
||||
finally:
|
||||
t1 = time.perf_counter()
|
||||
logger.debug("[RunCommand] Execution took {0}s for {1} in {2}".format(t1 - t0, commands, cwd))
|
||||
|
||||
|
||||
def run_command(
|
||||
commands, cwd=None, stderr=subprocess.STDOUT, shell=False, stream_stdout=True, throw_on_retcode=True, logger=None
|
||||
):
|
||||
return _run_command(
|
||||
commands,
|
||||
cwd=cwd,
|
||||
stderr=stderr,
|
||||
shell=shell,
|
||||
stream_stdout=stream_stdout,
|
||||
throw_on_retcode=throw_on_retcode,
|
||||
logger=logger,
|
||||
)
|
||||
Reference in New Issue
Block a user