Skip to content

Add support for python 3.12+ and drop support for 3.7 #1950

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

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 1 addition & 14 deletions .github/workflows/installation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
fail-fast: false
matrix:
os: ['ubuntu-latest', 'macos-latest', 'windows-latest']
python-version: ['3.8', '3.9', '3.10', '3.11']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
method: ['install' ,'pip']

include:
Expand All @@ -55,19 +55,6 @@ jobs:
method: pip
REZ_SET_PATH_COMMAND: 'export PATH="$PATH:~/rez/bin" PYTHONPATH=$PYTHONPATH:$HOME/rez'
REZ_INSTALL_COMMAND: pip install --target ~/rez .
# macOS
# Python 3.7 is not supported on Apple Silicon.
# macos-13 is the last macos runner image to run on Intel CPUs.
- os: macos-13
python-version: '3.7'
method: install
REZ_SET_PATH_COMMAND: 'export PATH=${PATH}:~/rez/bin/rez'
REZ_INSTALL_COMMAND: python ./install.py ~/rez
- os: macos-13
python-version: '3.7'
method: pip
REZ_SET_PATH_COMMAND: 'export PATH="$PATH:~/rez/bin" PYTHONPATH=$PYTHONPATH:$HOME/rez'
REZ_INSTALL_COMMAND: pip install --target ~/rez .
# windows
- os: windows-latest
method: install
Expand Down
8 changes: 1 addition & 7 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
strategy:
matrix:
os: ['macos-latest', 'ubuntu-latest', 'windows-latest']
python-version: ['3.8', '3.9', '3.10', '3.11']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
include:
- os: macos-latest
shells: 'sh,csh,bash,tcsh,zsh,pwsh'
Expand All @@ -49,12 +49,6 @@ jobs:
- os: windows-latest
shells: 'cmd,pwsh,gitbash'
rez-path: \installdir\Scripts\rez
# Python 3.7 is not supported on Apple Silicon.
# macos-13 is the last macos runner image to run on Intel CPUs.
- os: macos-13
shells: 'sh,csh,bash,tcsh,zsh,pwsh'
rez-path: /installdir/bin/rez
python-version: '3.7'

fail-fast: false

Expand Down
2 changes: 1 addition & 1 deletion .sonarcloud.properties
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ sonar.sources=src
sonar.exclusions=src/build_utils/**,src/rez/data/**,src/rez/tests/**,src/rez/vendor/**

# Misc properties
sonar.python.version=3.7, 3.8, 3.9, 3.10, 3.11, 3.12
sonar.python.version=3.8, 3.9, 3.10, 3.11, 3.12
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ Resolved environments can also be created via the API:

## Quickstart

First, install Rez using Python 3.7+. Download the source, and from the source directory, run
First, install Rez using Python 3.8+. Download the source, and from the source directory, run
(with DEST_DIR replaced with your install location):

]$ python3 ./install.py -v DEST_DIR
Expand Down
2 changes: 1 addition & 1 deletion docs/source/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Installation Script

To install rez, you will need:

1. Python 3.7 or above. We support 3.7, 3.8, 3.9, 3.10 and 3.11.
1. Python 3.8 or above. We support 3.8, 3.9, 3.10, 3.11, 3.12 and 3.13.
The python interpreter you use to run the install script will be the interpreter
used by rez itself.
2. The source code. You can get it by either cloning the `repository <https://github.com/AcademySoftwareFoundation/rez>`_
Expand Down
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def find_files(pattern, path=None, root="rez"):
packages=find_packages('src', exclude=["build_utils",
"build_utils.*",
"tests"]),
install_requires=[],
package_data={
'rez':
['utils/logging.conf'] +
Expand All @@ -88,13 +89,14 @@ def find_files(pattern, path=None, root="rez"):
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development",
"Topic :: System :: Software Distribution"
],
python_requires=">=3.7",
python_requires=">=3.8,<3.14",
)
2 changes: 1 addition & 1 deletion src/rez/bind/rez.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def make_root(variant, root):
with make_package("rez", path, make_root=make_root) as pkg:
pkg.version = version
pkg.commands = commands
pkg.requires = ["python-3.7+<3.12"]
pkg.requires = ["python-3.8+<3.14"]
pkg.variants = [system.variant]

return pkg.installed_variants
2 changes: 1 addition & 1 deletion src/rez/cli/selftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def __call__(self, parser, namespace, values, option_string=None):
prefix = "test_"
for importer, name, ispkg in iter_modules([tests_dir]):
if not ispkg and name.startswith(prefix):
module = importer.find_module(name).load_module(name)
module = importer.find_spec(name).loader.load_module(name)
name_ = name[len(prefix):]
all_module_tests.append(name_)
tests.append((name_, module))
Expand Down
4 changes: 2 additions & 2 deletions src/rez/plugin_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ def load_plugins(self):
# already loaded, so check for that
plugin_module = sys.modules.get(modname)
if plugin_module is None:
loader = importer.find_module(modname)
plugin_module = loader.load_module(modname)
loader = importer.find_spec(modname)
plugin_module = loader.loader.load_module(modname)

elif os.path.dirname(plugin_module.__file__) != path:
if config.debug("plugins"):
Expand Down
4 changes: 2 additions & 2 deletions src/rez/rezconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Copyright Contributors to the Rez Project


"""
r"""
Rez configuration settings. Do not change this file.

Settings are determined in the following way (higher number means higher
Expand Down Expand Up @@ -38,7 +38,7 @@

Paths should use the path separator appropriate for the operating system
(based on Python's os.path.sep). So for Linux paths, / should be used. On
Windows \ (unescaped) should be used.
Windows \ (unescaped!) should be used.

Note: The comments in this file are extracted and turned into documentation. Pay
attention to the comment formatting and follow the existing style closely.
Expand Down
61 changes: 52 additions & 9 deletions src/rez/utils/pip.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
"""
Python packaging related utilities.
"""
import sys
import os.path
from importlib.metadata import Distribution, DistributionFinder
from email.parser import Parser

import pkg_resources
from pathlib import Path

from rez.vendor.packaging.version import (
parse as packaging_parse,
Expand Down Expand Up @@ -326,8 +327,14 @@ def is_pure_python_package(installed_dist):
setuptools_dist = convert_distlib_to_setuptools(installed_dist)

# see https://www.python.org/dev/peps/pep-0566/#json-compatible-metadata
wheel_data = setuptools_dist.get_metadata('WHEEL')
wheel_data = Parser().parsestr(wheel_data)

wheel_data = None
for f in setuptools_dist.files:
if f.name == "WHEEL":
wheel_data = f.read_text()
break

wheel_data = Parser().parsestr(wheel_data) if wheel_data else {}

# see https://www.python.org/dev/peps/pep-0427/#what-s-the-deal-with-purelib-vs-platlib
is_purelib = wheel_data.get("Root-Is-Purelib", "").lower()
Expand All @@ -346,8 +353,16 @@ def is_entry_points_scripts_package(installed_dist):
"""
setuptools_dist = convert_distlib_to_setuptools(installed_dist)

entry_map = setuptools_dist.get_entry_map()
return bool(entry_map.get("console_scripts") or entry_map.get("gui_scripts"))
if sys.version_info < (3, 10):
for entry_point in setuptools_dist.entry_points:
if entry_point.group in ["console_scripts", "gui_scripts"]:
return True
return False

console_scripts = setuptools_dist.entry_points.select(group="console_scripts")
gui_scripts = setuptools_dist.entry_points.select(group="gui_scripts")

return bool(console_scripts or gui_scripts)


def get_rez_requirements(installed_dist, python_version, name_casings=None):
Expand Down Expand Up @@ -511,13 +526,41 @@ def convert_distlib_to_setuptools(installed_dist):
to convert.

Returns:
`pkg_resources.DistInfoDistribution`: Equivalent setuptools dist object.
`Distribution`: Equivalent importlib/setuptools dist object.
"""

# Localized from pkg_resources.safe_name
def safe_name(name: str) -> str:
import re
"""Convert an arbitrary string to a standard distribution name

Any runs of non-alphanumeric/. characters are replaced with a single '-'.
"""
return re.sub('[^A-Za-z0-9.]+', '-', name)

class DirectPathScanner(DistributionFinder):
@classmethod
def find_distributions(cls, context=DistributionFinder.Context()):
for search_path in context.path:
for entry in os.scandir(search_path):
if entry.is_dir() and (entry.name.endswith(".dist-info") or entry.name.endswith(".egg-info")):
yield Distribution.at(Path(entry.path))

path = os.path.dirname(installed_dist.path)
setuptools_dists = pkg_resources.find_distributions(path)

setuptools_dists = list(
DirectPathScanner.find_distributions(
context=DistributionFinder.Context(path=[path])
)
)

for setuptools_dist in setuptools_dists:
if setuptools_dist.key == pkg_resources.safe_name(installed_dist.key):
if sys.version_info < (3, 10):
name = setuptools_dist.metadata["name"]
else:
name = setuptools_dist.name

if name == safe_name(installed_dist.key):
return setuptools_dist

return None
Expand Down
8 changes: 4 additions & 4 deletions src/rez/utils/py_dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Functions for converting python distributions to rez packages.
"""
from rez.exceptions import RezSystemError
import pkg_resources
import importlib.metadata as importlib_metadata
import shutil
import sys
import os
Expand Down Expand Up @@ -92,7 +92,7 @@ def get_dist_dependencies(name, recurse=True):
@returns A set of package names.
@note The first entry in the list is always the top-level package itself.
"""
dist = pkg_resources.get_distribution(name)
dist = importlib_metadata.Distribution.from_name(name)
pkg_name = convert_name(dist.project_name)

reqs = set()
Expand All @@ -102,7 +102,7 @@ def get_dist_dependencies(name, recurse=True):
while working:
deps = set()
for distname in working:
dist = pkg_resources.get_distribution(distname)
dist = importlib_metadata.Distribution.from_name(distname)
pkg_name = convert_name(dist.project_name)
reqs.add(pkg_name)

Expand Down Expand Up @@ -141,7 +141,7 @@ def convert_dist(name, dest_path, make_variant=True, ignore_dirs=None,
Returns:
Install path of the new Rez package.
"""
dist = pkg_resources.get_distribution(name)
dist = importlib_metadata.distribution(name)
pkg_name = convert_name(dist.project_name)
pkg_version = convert_version(dist.version)

Expand Down
Loading