-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Prototype uv
script tox
replacement
#41775
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
9ffb371
remove all usage of deprecating pkg_resources
scbedd f1cd1fa
fix test
scbedd cadbd0e
fix issues with test_parse_functionality
scbedd 8928d38
Merge branch 'main' into remove-pkg-resources
scbedd 47d2bbb
remove usage of Requirement.key in favor of Requirement.name
scbedd 1e1c817
hopefully last fixes?
scbedd 6b4ff0d
fix remaining issue with analyze_dependency as well
scbedd 8634ab5
touch a few more files. dependencies.py and get_installed_pacackages …
scbedd 1127a15
Merge branch 'remove-pkg-resources' into feature/replace-tox
scbedd 8b4f79e
progress. we need to have gotten rid of pkg_resources though so let's…
scbedd a473fbf
Merge branch 'main' into feature/replace-tox
scbedd f685452
further updates
scbedd 6f12aa4
getting much closer
scbedd 465e3b5
save additional readme update
scbedd a07f687
rename whl to azpy-whl
scbedd 2334887
fix the download even if pip download is slow in comparison uv (#41801)
scbedd File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Readme for azure-sdk-tools | ||
|
||
This will be the location of the moved azure-sdk-tools after it's moved from `tools/azure-sdk-tools` to here. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# `uv` script checks for `azure-sdk-for-python` | ||
|
||
The scripts contained within this directory are self-contained validation scripts that are intended to be used by CI and for local development. | ||
|
||
## Example Check Invocations | ||
|
||
- `uv run tools/ci/uv/<checkname>.py azure-core` | ||
- `uv run tools/ci/uv/<checkname>.py azure-storage*` | ||
- `uv run tools/ci/uv/<checkname>.py azure-storage-blob,azure-storage-queue` | ||
|
||
## Outstanding questions | ||
|
||
### Using tool instead of script? | ||
|
||
- Should we instead create a root `pyproject.toml` for each of these and install as a `uv tool`? | ||
- ``` | ||
tools/ | ||
ci/ | ||
uv/ | ||
whl/ | ||
whl.py | ||
pyproject.toml | ||
``` | ||
- Doing the above will allow us to install the check as a `tool` (which will have an isolated venv) and enable easy access via a named entrypoint on the PATH. | ||
- We will need to be more explicit about cleaning up the venv being used in CI, versus `uv run` which is purely ephemeral unless told otherwise. | ||
- Another option would be to: | ||
- ``` | ||
tools/ | ||
ci/ | ||
uv/ | ||
whl.py | ||
mindependency.py | ||
pyproject.toml -> reference both | ||
``` | ||
|
||
### How do I debug when I'm modifying a new `uv` check? | ||
|
||
todo: once scbedd r someone else has a good story for this in vscode + pylance. | ||
|
||
### Is there a good way to pass on custom arguments that are after the -- in the uv script invocation | ||
|
||
todo: discover this | ||
|
||
## Guidelines for creating a uv check | ||
|
||
- You **must** include `uv` in the list of dependencies for your script. This will enable access to `uv pip` from within the environment without assuming anything about a global `uv` install. This will enable `subprocess.run([sys.executable, '-m', 'uv', 'pip', 'install', 'packagename'])` with all the efficiency implied by using `uv`. | ||
- You **must** `uv add --script tools/ci/uv/whl.py "-r eng/ci_tools.txt"` to ensure that the generic pinned dependencies are present on your script. | ||
- You **must** preface your scripts with `azpy`. So the `whl` check would be `azpy-whl.py`. | ||
- You **must** cleave to the common argparse definition to ensure that all the checks have similar entrypoint behavior. | ||
- This does not exist yet. | ||
|
||
## Transition from `tox` details | ||
|
||
### Pros | ||
|
||
- The speed is astonishing | ||
- We get ephemeral venvs by default | ||
- We can encode a ton more information due to the freedom of a pure-python script invoking all the work. | ||
|
||
### Cons | ||
|
||
- A single unified dependency file is not supported. When we update we will just have to `uv add --script <check.py> -r eng/ci_tools.txt` which will update all the PEP723 preambles for our check scripts. | ||
- We will need to parallelize invocation of these environments ourselves and deal with any gotchas. We'll do that in `dispatch_uv.py` which will be a follow-up to `dispatch_tox.py`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
#!/usr/bin/env -S uv run --script | ||
# /// script | ||
# requires-python = ">=3.9" | ||
# dependencies = [ | ||
# "wheel==0.45.1", | ||
# "packaging==24.2", | ||
# "urllib3==2.2.3", | ||
# "tomli", | ||
# "build==1.2.2.post1", | ||
# "pytest==8.3.5", | ||
# "pytest-cov==5.0.0", | ||
# "azure-sdk-tools", | ||
# "setuptools", | ||
# "pytest-asyncio==0.24.0", | ||
# "pytest-custom-exit-code==0.3.0", | ||
# "pytest-xdist==3.2.1", | ||
# "coverage==7.6.1", | ||
# "bandit==1.6.2", | ||
# "pyproject-api==1.8.0", | ||
# "jinja2==3.1.6", | ||
# "json-delta==2.0.2", | ||
# "readme-renderer==43.0", | ||
# "python-dotenv==1.0.1", | ||
# "pyyaml==6.0.2", | ||
# "six==1.17.0", | ||
# "uv", | ||
# ] | ||
# | ||
# [tool.uv.sources] | ||
# azure-sdk-tools = { path = "../../../../tools/azure-sdk-tools", editable = true } | ||
# /// | ||
|
||
import os | ||
import argparse | ||
|
||
from ci_tools.functions import discover_targeted_packages | ||
|
||
from ci_tools.uv import install_uv_devrequirements, set_environment_variable_defaults, DEFAULT_ENVIRONMENT_VARIABLES, uv_pytest | ||
from ci_tools.variables import discover_repo_root | ||
from ci_tools.scenario.generation import create_package_and_install | ||
import tempfile | ||
import shutil | ||
import os | ||
|
||
def main(): | ||
parser = argparse.ArgumentParser( | ||
description="Run dev tooling against a given package directory" | ||
) | ||
parser.add_argument( | ||
"target", | ||
nargs="?", | ||
default="azure-core", | ||
help="Path to the target package folder (default: current directory)", | ||
) | ||
args = parser.parse_args() | ||
|
||
set_environment_variable_defaults(DEFAULT_ENVIRONMENT_VARIABLES) | ||
|
||
# todo, we should use os.cwd as the target if no target is provided | ||
# with some validation to ensure we're in a package directory (eg setup.py or pyproject.toml exists) and not repo root. | ||
target_root_dir=discover_repo_root() | ||
targeted = discover_targeted_packages(args.target, target_root_dir) | ||
|
||
failed = False | ||
|
||
for pkg in targeted: | ||
install_uv_devrequirements( | ||
pkg_path=pkg, | ||
allow_nonpresence=True, | ||
) | ||
|
||
staging_area = tempfile.mkdtemp() | ||
|
||
create_package_and_install( | ||
distribution_directory=staging_area, | ||
target_setup=pkg, | ||
skip_install=False, | ||
cache_dir=None, | ||
work_dir=staging_area, | ||
force_create=False, | ||
package_type="wheel", | ||
pre_download_disabled=False, | ||
) | ||
|
||
# todo, come up with a good pattern for passing all the additional args after -- to pytest | ||
result = uv_pytest(target_path=pkg, additional_args=["--cov", "--cov-report=xml", "--cov-report=html"]) | ||
|
||
if __name__ == "__main__": | ||
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,4 +94,4 @@ | |
) | ||
|
||
|
||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from .manage import install_uv_devrequirements | ||
from .prepare import set_environment_variable_defaults, DEFAULT_ENVIRONMENT_VARIABLES | ||
from .invoke import uv_pytest | ||
|
||
__all__ = ["install_uv_devrequirements", "set_environment_variable_defaults", "DEFAULT_ENVIRONMENT_VARIABLES", "uv_pytest"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import subprocess | ||
import logging | ||
|
||
from pytest import main as pytest_main | ||
|
||
from ci_tools.functions import is_error_code_5_allowed | ||
|
||
def uv_pytest(target_path: str, additional_args: list[str] = [], shell: bool = False) -> bool: | ||
logging.info(f"Invoke pytest for {target_path}") | ||
|
||
exit_code = 0 | ||
|
||
if shell: | ||
result = subprocess.run( | ||
[sys.executable, "-m", "pytest", target_path] + additional_args, | ||
check=False | ||
) | ||
exit_code = result.returncode | ||
else: | ||
exit_code = pytest_main( | ||
[target_path] + additional_args | ||
) | ||
|
||
if exit_code != 0: | ||
if exit_code == 5 and is_error_code_5_allowed(): | ||
logging.info("Exit code 5 is allowed, continuing execution.") | ||
return True | ||
else: | ||
logging.info(f"pytest failed with exit code {exit_code}.") | ||
return False | ||
|
||
return True |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import os | ||
import logging | ||
import subprocess | ||
import sys | ||
|
||
def install_uv_devrequirements(pkg_path: str, allow_nonpresence: bool = False): | ||
""" | ||
Installs the development requirements for a given package. | ||
|
||
Args: | ||
pkg_path (str): The path to the package directory. | ||
""" | ||
|
||
dev_req_path = os.path.join(pkg_path, "dev_requirements.txt") | ||
|
||
if not os.path.exists(dev_req_path) and not allow_nonpresence: | ||
print(f"Development requirements file not found at {dev_req_path}") | ||
raise FileNotFoundError(f"Development requirements file not found at {dev_req_path}") | ||
|
||
try: | ||
command = [sys.executable, "-m", "uv", "pip", "install", "-r", dev_req_path] | ||
subprocess.run(command, check=True, cwd=pkg_path) | ||
logging.info(f"Successfully installed development requirements from {dev_req_path}.") | ||
except subprocess.CalledProcessError as e: | ||
raise RuntimeError(f"Failed to install development requirements: {e}") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
from typing import Dict | ||
import os | ||
|
||
DEFAULT_ENVIRONMENT_VARIABLES = { | ||
"SPHINX_APIDOC_OPTIONS": "members,undoc-members,inherited-members", | ||
"PROXY_URL": "http://localhost:5000", | ||
"VIRTUALENV_WHEEL": "0.45.1", | ||
"VIRTUALENV_PIP": "24.0", | ||
"VIRTUALENV_SETUPTOOLS": "75.3.2", | ||
"PIP_EXTRA_INDEX_URL": "https://pypi.python.org/simple", | ||
# I haven't spent much time looking to see if a variable exists when invoking uv run. there might be one already that we can depend | ||
# on for get_pip_command adjustment. | ||
"IN_UV": "1" | ||
} | ||
|
||
def set_environment_variable_defaults(settings: Dict[str, str]) -> None: | ||
""" | ||
Sets default environment variables for the UV prepare script. | ||
|
||
Args: | ||
settings (Dict[str, str]): A dictionary of environment variable names and their default values. | ||
""" | ||
for key, value in settings.items(): | ||
if key not in os.environ: | ||
os.environ.setdefault(key, value) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accidentally added will be backed out.