Skip to content

Commit 5c3c178

Browse files
scbeddmccoyp
andauthored
Support pure pyproject-toml packages (Azure#37882)
* add support for parsing and building pure pyproject.toml packages within azure-sdk-tools * various typing updates across azure-sdk-tools, cleaning up what code paths I'm touching with the new build and parse support * add tests for various pyproject.toml parse scenarios * bump setuptools to 74.1.3 to support pure-pyproject.toml extension packages (eg without a setup.py) --------- Co-authored-by: McCoy Patiño <[email protected]>
1 parent b53b7bb commit 5c3c178

File tree

37 files changed

+582
-66
lines changed

37 files changed

+582
-66
lines changed

eng/ci_tools.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# requirements leveraged by ci tools
2-
setuptools==72.2.0
2+
setuptools==74.1.3
33
virtualenv==20.25.1
44
wheel==0.43.0
55
packaging==23.1

eng/conda_test_requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ trio
88
typing_extensions>=3.7.2
99
cryptography
1010
adal
11-
setuptools==72.2.0
11+
setuptools==74.1.3
1212
pytest-asyncio==0.12.0
1313
-e sdk/core/azure-core/tests/testserver_tests/coretestserver
1414
azure-mgmt-storage

eng/pipelines/templates/jobs/tests-nightly-python.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ jobs:
6464
export PATH=~/.local/bin:$PATH
6565
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
6666
python3 get-pip.py
67-
python3 -m pip install setuptools==72.2.0 wheel
67+
python3 -m pip install setuptools==74.1.3 wheel
6868
python3 -m pip install tox packaging twine beautifulsoup4
6969
python3 --version
7070
cd $(Build.SourcesDirectory)

eng/regression_tools.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# requirements leveraged by ci tools
2-
setuptools==72.2.0
2+
setuptools==74.1.3
33
virtualenv==20.23.0
44
wheel==0.43.0
55
Jinja2==3.1.2
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
httpx==0.25.2
22
markdown==3.6
33
PyGitHub>=1.59.0
4-
setuptools==72.2.0
4+
setuptools==74.1.3
55
-e ./sdk/identity/azure-identity
66
-e ./tools/azure-sdk-tools

sdk/ml/azure-ai-ml/dev_requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pytest-mock
1212
pytest
1313
pydash
1414
azure-mgmt-msi
15-
pywin32==304 ; sys_platform == 'win32'
15+
pywin32==306 ; sys_platform == 'win32'
1616
docker;platform.python_implementation!="PyPy"
1717
numpy;platform.python_implementation!="PyPy"
1818
scikit-image;platform.python_implementation!="PyPy"

tools/azure-sdk-tools/ci_tools/build.py

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
from subprocess import run
44

5-
from typing import List
6-
from ci_tools.functions import discover_targeted_packages, process_requires
7-
from ci_tools.parsing import ParsedSetup
5+
from typing import List, Optional
6+
from ci_tools.functions import discover_targeted_packages, process_requires, get_pip_list_output
7+
from ci_tools.parsing import ParsedSetup, parse_require
88
from ci_tools.variables import DEFAULT_BUILD_ID, str_to_bool, discover_repo_root, get_artifact_directory
99
from ci_tools.versioning.version_shared import set_version_py, set_dev_classifier
1010
from ci_tools.versioning.version_set_dev import get_dev_version, format_build_id
@@ -84,7 +84,7 @@ def build() -> None:
8484
help=(
8585
"Where is the start directory that we are building against? If not provided, the current working directory will be used. Please ensure you are within the azure-sdk-for-python repository."
8686
),
87-
),
87+
)
8888

8989
parser.add_argument(
9090
"--build_id",
@@ -124,7 +124,6 @@ def build() -> None:
124124
targeted_packages,
125125
artifact_directory,
126126
str_to_bool(args.is_dev_build),
127-
str_to_bool(args.apiview_closure),
128127
build_id,
129128
)
130129

@@ -144,17 +143,20 @@ def cleanup_build_artifacts(build_folder):
144143

145144
def build_packages(
146145
targeted_packages: List[str],
147-
distribution_directory: str = None,
146+
distribution_directory: Optional[str] = None,
148147
is_dev_build: bool = False,
149-
build_apiview_artifact: bool = False,
150148
build_id: str = "",
151149
):
152150
logging.log(level=logging.INFO, msg=f"Generating {targeted_packages} using python{sys.version}")
153151

154152
for package_root in targeted_packages:
155153
setup_parsed = ParsedSetup.from_path(package_root)
156154
package_name_in_artifacts = os.path.join(os.path.basename(package_root))
157-
dist_dir = os.path.join(distribution_directory, package_name_in_artifacts)
155+
156+
if distribution_directory:
157+
dist_dir = os.path.join(distribution_directory, package_name_in_artifacts)
158+
else:
159+
dist_dir = package_name_in_artifacts
158160

159161
if is_dev_build:
160162
process_requires(package_root, True)
@@ -170,7 +172,7 @@ def build_packages(
170172

171173

172174
def create_package(
173-
setup_directory_or_file: str, dest_folder: str = None, enable_wheel: bool = True, enable_sdist: bool = True
175+
setup_directory_or_file: str, dest_folder: str, enable_wheel: bool = True, enable_sdist: bool = True
174176
):
175177
"""
176178
Uses the invoking python executable to build a wheel and sdist file given a setup.py or setup.py directory. Outputs
@@ -180,11 +182,24 @@ def create_package(
180182
dist = get_artifact_directory(dest_folder)
181183
setup_parsed = ParsedSetup.from_path(setup_directory_or_file)
182184

183-
if enable_wheel:
184-
if setup_parsed.ext_modules:
185-
run([sys.executable, "-m", "cibuildwheel", "--output-dir", dist], cwd=setup_parsed.folder, check=True)
186-
else:
187-
run([sys.executable, "setup.py", "bdist_wheel", "-d", dist], cwd=setup_parsed.folder, check=True)
188-
189-
if enable_sdist:
190-
run([sys.executable, "setup.py", "sdist", "-d", dist], cwd=setup_parsed.folder, check=True)
185+
if setup_parsed.is_pyproject:
186+
# when building with pyproject, we will use `python -m build` to build the package
187+
# -n argument will not use an isolated environment, which means the current environment must have all the dependencies of the package installed, to successfully
188+
# pull in the dynamic `__version__` attribute. This is because setuptools is actually walking the __init__.py to get that attribute, which will fail
189+
# if the imports within the setup.py don't work. Perhaps an isolated environment is better, pulling all the "dependencies" into the [build-system].requires list
190+
191+
# given the additional requirements of the package, we should install them in the current environment before attempting to build the package
192+
# we assume the presence of `wheel`, `build`, `setuptools>=61.0.0`
193+
pip_output = get_pip_list_output(sys.executable)
194+
necessary_install_requirements = [req for req in setup_parsed.requires if parse_require(req).key not in pip_output.keys()]
195+
run([sys.executable, "-m", "pip", "install", *necessary_install_requirements], cwd=setup_parsed.folder)
196+
run([sys.executable, "-m", "build", f"-n{'s' if enable_sdist else ''}{'w' if enable_wheel else ''}", "-o", dist], cwd=setup_parsed.folder)
197+
else:
198+
if enable_wheel:
199+
if setup_parsed.ext_modules:
200+
run([sys.executable, "-m", "cibuildwheel", "--output-dir", dist], cwd=setup_parsed.folder, check=True)
201+
else:
202+
run([sys.executable, "setup.py", "bdist_wheel", "-d", dist], cwd=setup_parsed.folder, check=True)
203+
204+
if enable_sdist:
205+
run([sys.executable, "setup.py", "sdist", "-d", dist], cwd=setup_parsed.folder, check=True)

tools/azure-sdk-tools/ci_tools/functions.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from pkg_resources import Requirement
1111

1212
from ci_tools.variables import discover_repo_root, DEV_BUILD_IDENTIFIER, str_to_bool
13-
from ci_tools.parsing import ParsedSetup, get_config_setting
13+
from ci_tools.parsing import ParsedSetup, get_config_setting, get_pyproject
1414
from pypi_tools.pypi import PyPIClient
1515

1616
import os, sys, platform, glob, re, logging
@@ -166,6 +166,15 @@ def glob_packages(glob_string: str, target_root_dir: str) -> List[str]:
166166
)
167167
collected_top_level_directories.extend([os.path.dirname(p) for p in globbed])
168168

169+
# handle pyproject.toml separately, as we need to filter them by the presence of a `[project]` section
170+
for glob_string in individual_globs:
171+
globbed = glob.glob(os.path.join(target_root_dir, glob_string, "pyproject.toml")) + glob.glob(
172+
os.path.join(target_root_dir, "sdk/*/", glob_string, "pyproject.toml")
173+
)
174+
for p in globbed:
175+
if get_pyproject(os.path.dirname(p)):
176+
collected_top_level_directories.append(os.path.dirname(p))
177+
169178
# deduplicate, in case we have double coverage from the glob strings. Example: "azure-mgmt-keyvault,azure-mgmt-*"
170179
return list(set(collected_top_level_directories))
171180

@@ -249,10 +258,10 @@ def apply_inactive_filter(collected_packages: List[str]) -> List[str]:
249258
return packages
250259

251260

252-
def update_requires(setup_py_path, requires_dict):
261+
def update_requires(setup_path, requires_dict):
253262
# This method changes package requirement by overriding the specifier
254263
contents = []
255-
with open(setup_py_path, "r") as setup_file:
264+
with open(setup_path, "r") as setup_file:
256265
contents = setup_file.readlines()
257266

258267
# find and replace all existing package requirement with new requirement
@@ -261,7 +270,7 @@ def update_requires(setup_py_path, requires_dict):
261270
for key in keys:
262271
contents[i] = contents[i].replace(key, requires_dict[key])
263272

264-
with open(setup_py_path, "w") as setup_file:
273+
with open(setup_path, "w") as setup_file:
265274
setup_file.writelines(contents)
266275

267276

tools/azure-sdk-tools/ci_tools/parsing/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
update_build_config,
1010
compare_string_to_glob_array,
1111
get_ci_config,
12+
get_version_py,
13+
get_pyproject,
14+
VERSION_REGEX,
15+
VERSION_PY,
16+
OLD_VERSION_PY
1217
)
1318

1419
__all__ = [
@@ -22,4 +27,9 @@
2227
"update_build_config",
2328
"compare_string_to_glob_array",
2429
"get_ci_config",
30+
"get_version_py",
31+
"get_pyproject",
32+
"VERSION_REGEX",
33+
"VERSION_PY",
34+
"OLD_VERSION_PY"
2535
]

0 commit comments

Comments
 (0)