Skip to content
This repository was archived by the owner on May 22, 2026. It is now read-only.

Commit 69720a2

Browse files
author
iscai-msft
committed
Merge branch 'main' of https://github.com/Azure/autorest.python into azsdk-pool
2 parents 0625875 + c1a85c5 commit 69720a2

67 files changed

Lines changed: 1895 additions & 77 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/autorest.python/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Release
22

3+
## 6.35.5
4+
5+
No changes, version bump only.
6+
37
## 6.35.4
48

59
### Bump dependencies

packages/autorest.python/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@autorest/python",
3-
"version": "6.35.4",
3+
"version": "6.35.5",
44
"description": "The Python extension for generators in AutoRest.",
55
"scripts": {
66
"start": "node ./scripts/run-python3.js ./scripts/start.py",
@@ -29,7 +29,7 @@
2929
},
3030
"homepage": "https://github.com/Azure/autorest.python/blob/main/README.md",
3131
"dependencies": {
32-
"@typespec/http-client-python": "~0.12.4",
32+
"@typespec/http-client-python": "~0.12.5",
3333
"@autorest/system-requirements": "~1.0.2",
3434
"fs-extra": "~11.2.0",
3535
"tsx": "~4.19.1"

packages/typespec-python/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Release
22

3+
## 0.45.5
4+
5+
### Features
6+
7+
- [#3116](https://github.com/Azure/autorest.python/pull/3116) [typespec-python] Add support for uv package manager alongside pip
8+
9+
310
## 0.45.4
411

512
### Bump dependencies

packages/typespec-python/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@azure-tools/typespec-python",
3-
"version": "0.45.4",
3+
"version": "0.45.5",
44
"author": "Microsoft Corporation",
55
"description": "TypeSpec emitter for Python SDKs",
66
"homepage": "https://github.com/Azure/autorest.python",
@@ -67,7 +67,7 @@
6767
"js-yaml": "~4.1.0",
6868
"semver": "~7.6.2",
6969
"tsx": "~4.19.1",
70-
"@typespec/http-client-python": "~0.12.4",
70+
"@typespec/http-client-python": "~0.12.5",
7171
"fs-extra": "~11.2.0"
7272
},
7373
"devDependencies": {

packages/typespec-python/scripts/install.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,37 +11,35 @@
1111
raise Exception("Autorest for Python extension requires Python 3.9 at least")
1212

1313
try:
14-
import pip
15-
except ImportError:
16-
raise Exception("Your Python installation doesn't have pip available")
14+
from package_manager import detect_package_manager, PackageManagerNotFoundError
15+
16+
detect_package_manager() # Just check if we have a package manager
17+
except (ImportError, ModuleNotFoundError, PackageManagerNotFoundError):
18+
raise Exception("Your Python installation doesn't have a suitable package manager (pip or uv) available")
1719

1820
try:
1921
import venv
2022
except ImportError:
2123
raise Exception("Your Python installation doesn't have venv available")
2224

2325

24-
# Now we have pip and Py >= 3.9, go to work
26+
# Now we have a package manager (uv or pip) and Py >= 3.9, go to work
2527

2628
from pathlib import Path
2729

28-
from venvtools import ExtendedEnvBuilder, python_run
29-
3030
_ROOT_DIR = Path(__file__).parent.parent
3131

3232

3333
def main():
3434
venv_path = _ROOT_DIR / "venv"
35-
if venv_path.exists():
36-
env_builder = venv.EnvBuilder(with_pip=True)
37-
venv_context = env_builder.ensure_directories(venv_path)
38-
else:
39-
env_builder = ExtendedEnvBuilder(with_pip=True, upgrade_deps=True)
40-
env_builder.create(venv_path)
41-
venv_context = env_builder.context
42-
43-
python_run(venv_context, "pip", ["install", "-U", "pip"])
44-
python_run(venv_context, "pip", ["install", "-U", "black"])
35+
36+
# Create virtual environment using package manager abstraction
37+
from package_manager import create_venv_with_package_manager, install_packages
38+
39+
venv_context = create_venv_with_package_manager(venv_path)
40+
41+
# Install required packages - install_packages handles package manager logic
42+
install_packages(["-U", "black"], venv_context)
4543

4644

4745
if __name__ == "__main__":
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/usr/bin/env python
2+
3+
# -------------------------------------------------------------------------
4+
# Copyright (c) Microsoft Corporation. All rights reserved.
5+
# Licensed under the MIT License. See License.txt in the project root for
6+
# license information.
7+
# --------------------------------------------------------------------------
8+
"""Package manager utilities for detecting and using pip or uv."""
9+
10+
import subprocess
11+
import sys
12+
import venv
13+
from pathlib import Path
14+
from venvtools import ExtendedEnvBuilder
15+
16+
17+
class PackageManagerNotFoundError(Exception):
18+
"""Raised when no suitable package manager is found."""
19+
20+
pass
21+
22+
23+
def _check_command_available(command: str) -> bool:
24+
"""Check if a command is available in the environment."""
25+
try:
26+
subprocess.run([command, "--version"], capture_output=True, check=True)
27+
return True
28+
except (subprocess.CalledProcessError, FileNotFoundError):
29+
return False
30+
31+
32+
def detect_package_manager() -> str:
33+
"""Detect the best available package manager.
34+
35+
Returns:
36+
str: The package manager command ('uv' or 'pip')
37+
38+
Raises:
39+
PackageManagerNotFoundError: If no suitable package manager is found
40+
"""
41+
# Check for uv first since it's more modern and faster
42+
if _check_command_available("uv"):
43+
return "uv"
44+
45+
# Fall back to pip
46+
if _check_command_available("pip"):
47+
return "pip"
48+
49+
# As a last resort, try using python -m pip
50+
try:
51+
subprocess.run([sys.executable, "-m", "pip", "--version"], capture_output=True, check=True)
52+
return "python -m pip"
53+
except (subprocess.CalledProcessError, FileNotFoundError):
54+
pass
55+
56+
raise PackageManagerNotFoundError("No suitable package manager found. Please install either uv or pip.")
57+
58+
59+
def get_install_command(package_manager: str, venv_context=None) -> list:
60+
"""Get the install command for the given package manager.
61+
62+
Args:
63+
package_manager: The package manager command ('uv', 'pip', or 'python -m pip')
64+
venv_context: The virtual environment context (optional, used for pip)
65+
66+
Returns:
67+
list: The base install command as a list
68+
"""
69+
if package_manager == "uv":
70+
cmd = ["uv", "pip", "install"]
71+
if venv_context:
72+
cmd.extend(["--python", venv_context.env_exe])
73+
return cmd
74+
elif package_manager == "pip":
75+
if venv_context:
76+
return [venv_context.env_exe, "-m", "pip", "install"]
77+
else:
78+
return ["pip", "install"]
79+
elif package_manager == "python -m pip":
80+
if venv_context:
81+
return [venv_context.env_exe, "-m", "pip", "install"]
82+
else:
83+
return [sys.executable, "-m", "pip", "install"]
84+
else:
85+
raise ValueError(f"Unknown package manager: {package_manager}")
86+
87+
88+
def install_packages(packages: list, venv_context=None, package_manager: str = None) -> None:
89+
"""Install packages using the available package manager.
90+
91+
Args:
92+
packages: List of packages to install
93+
venv_context: Virtual environment context (optional)
94+
package_manager: Package manager to use (auto-detected if None)
95+
"""
96+
if package_manager is None:
97+
package_manager = detect_package_manager()
98+
99+
install_cmd = get_install_command(package_manager, venv_context)
100+
101+
try:
102+
subprocess.check_call(install_cmd + packages)
103+
except subprocess.CalledProcessError as e:
104+
raise RuntimeError(f"Failed to install packages with {package_manager}: {e}")
105+
106+
107+
def create_venv_with_package_manager(venv_path):
108+
"""Create virtual environment using the best available package manager.
109+
110+
Args:
111+
venv_path: Path where to create the virtual environment
112+
113+
Returns:
114+
venv_context: Virtual environment context object
115+
"""
116+
package_manager = detect_package_manager()
117+
118+
if package_manager == "uv":
119+
# Use uv to create and manage the virtual environment
120+
if not venv_path.exists():
121+
subprocess.check_call(["uv", "venv", str(venv_path)])
122+
123+
# Create a mock venv_context for compatibility
124+
class MockVenvContext:
125+
def __init__(self, venv_path):
126+
self.env_exe = (
127+
str(venv_path / "bin" / "python")
128+
if sys.platform != "win32"
129+
else str(venv_path / "Scripts" / "python.exe")
130+
)
131+
132+
return MockVenvContext(venv_path)
133+
else:
134+
# Use standard venv for pip
135+
if venv_path.exists():
136+
env_builder = venv.EnvBuilder(with_pip=True)
137+
return env_builder.ensure_directories(venv_path)
138+
else:
139+
env_builder = ExtendedEnvBuilder(with_pip=True, upgrade_deps=True)
140+
env_builder.create(venv_path)
141+
return env_builder.context

packages/typespec-python/scripts/prepare.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,12 @@
66
# license information.
77
# --------------------------------------------------------------------------
88
import sys
9-
import os
10-
import argparse
119

1210
if not sys.version_info >= (3, 9, 0):
1311
raise Exception("Autorest for Python extension requires Python 3.9 at least")
1412

1513
from pathlib import Path
16-
import venv
17-
18-
from venvtools import python_run
14+
from package_manager import create_venv_with_package_manager, install_packages
1915

2016
_ROOT_DIR = Path(__file__).parent.parent
2117

@@ -26,10 +22,10 @@ def main():
2622

2723
assert venv_preexists # Otherwise install was not done
2824

29-
env_builder = venv.EnvBuilder(with_pip=True)
30-
venv_context = env_builder.ensure_directories(venv_path)
25+
venv_context = create_venv_with_package_manager(venv_path)
26+
3127
try:
32-
python_run(venv_context, "pip", ["install", "-r", f"{_ROOT_DIR}/dev_requirements.txt"])
28+
install_packages(["-r", f"{_ROOT_DIR}/dev_requirements.txt"], venv_context)
3329
except FileNotFoundError as e:
3430
raise ValueError(e.filename)
3531

packages/typespec-python/scripts/run_tsp.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
# license information.
55
# --------------------------------------------------------------------------
66
import sys
7-
import venv
87
import logging
98
from pathlib import Path
109
from pygen import preprocess, codegen
1110
from pygen.utils import parse_args
11+
from package_manager import create_venv_with_package_manager
1212

1313
_ROOT_DIR = Path(__file__).parent.parent
1414

@@ -20,14 +20,13 @@
2020

2121
assert venv_preexists # Otherwise install was not done
2222

23-
env_builder = venv.EnvBuilder(with_pip=True)
24-
venv_context = env_builder.ensure_directories(venv_path)
23+
venv_context = create_venv_with_package_manager(venv_path)
2524

2625
if "--debug" in sys.argv or "--debug=true" in sys.argv:
2726
try:
2827
import debugpy # pylint: disable=import-outside-toplevel
2928
except ImportError:
30-
raise SystemExit("Please pip install ptvsd in order to use VSCode debugging")
29+
raise SystemExit("Please install ptvsd in order to use VSCode debugging")
3130

3231
# 5678 is the default attach port in the VS Code debug configurations
3332
debugpy.listen(("localhost", 5678))

packages/typespec-python/scripts/venvtools.py

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
# Licensed under the MIT License. See License.txt in the project root for
44
# license information.
55
# --------------------------------------------------------------------------
6-
from contextlib import contextmanager
7-
import tempfile
86
import subprocess
97
import venv
108
import sys
@@ -28,45 +26,10 @@ def ensure_directories(self, env_dir):
2826
return self.context
2927

3028

31-
def create(
32-
env_dir, system_site_packages=False, clear=False, symlinks=False, with_pip=False, prompt=None, upgrade_deps=False
33-
):
34-
"""Create a virtual environment in a directory."""
35-
builder = ExtendedEnvBuilder(
36-
system_site_packages=system_site_packages,
37-
clear=clear,
38-
symlinks=symlinks,
39-
with_pip=with_pip,
40-
prompt=prompt,
41-
upgrade_deps=upgrade_deps,
42-
)
43-
builder.create(env_dir)
44-
return builder.context
45-
46-
47-
@contextmanager
48-
def create_venv_with_package(packages):
49-
"""Create a venv with these packages in a temp dir and yield the env.
50-
51-
packages should be an iterable of pip version instructions (e.g. package~=1.2.3)
52-
"""
53-
with tempfile.TemporaryDirectory() as tempdir:
54-
myenv = create(tempdir, with_pip=True, upgrade_deps=True)
55-
pip_call = [
56-
myenv.env_exe,
57-
"-m",
58-
"pip",
59-
"install",
60-
]
61-
subprocess.check_call(pip_call + ["-U", "pip"])
62-
if packages:
63-
subprocess.check_call(pip_call + packages)
64-
yield myenv
65-
66-
6729
def python_run(venv_context, module, command=None, *, additional_dir="."):
6830
try:
6931
cmd_line = [venv_context.env_exe, "-m", module] + (command if command else [])
32+
7033
print("Executing: {}".format(" ".join(cmd_line)))
7134
subprocess.run(
7235
cmd_line,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Overview
2+
3+
This package is generated by `@typespec/http-client-python` with Typespec.
4+
5+
## Getting started
6+
7+
### Prequisites
8+
9+
- Python 3.9 or later is required to use this package.
10+
11+
### Install the package
12+
13+
Step into folder where setup.py is then run:
14+
15+
```bash
16+
pip install -e .
17+
```
18+
19+
### Examples
20+
21+
```python
22+
>>> from authentication.apikey import ApiKeyClient
23+
>>> from corehttp.exceptions import HttpResponseError
24+
25+
>>> client = ApiKeyClient(endpoint='<endpoint>')
26+
>>> try:
27+
<!-- write code here -->
28+
except HttpResponseError as e:
29+
print('service responds error: {}'.format(e.response.json()))
30+
```

0 commit comments

Comments
 (0)