Skip to content

Feat/add support for python 3.12+ reloaded #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 16 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
2 changes: 1 addition & 1 deletion .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 Down
2 changes: 1 addition & 1 deletion .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 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.7 or above. We support 3.7, 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
5 changes: 4 additions & 1 deletion 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=['setuptools'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this new PR, I think this is my only issue. I'll take the branch, erase setuptools on my local host, and see if I'm still able to install rez. If I can't, I do think we need to call it out on the Installation page. It's not exactly an onerous requirement, but a requirement nonetheless.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting - I erased setuptools on my local box, and it still installed... How the heck?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For visibility:

Up to Python 3.11 when creating a virtualenv setuptools is automatically installed into the virtualenv. Starting with 3.12 this is not the case anymore. By listing setuptools as an explicit dependency installing rez with pip automatlically installs the missing setuptools.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the corresponding github issue in python: python/cpython#95299
And it is also listed in the What's new in the docs: https://docs.python.org/3.12/whatsnew/3.12.html under "Important deprecations, removals or restrictions:"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting - I erased setuptools on my local box, and it still installed... How the heck?

@maxnbk install_requires means "dependencies at runtime", not "dependencies that are needed to install the project".

But now I'm curious, why do we need setuptools at runtime?

Copy link
Contributor Author

@instinct-vfx instinct-vfx May 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest i am not entirely sure we do, but i think we do. Here is an example of a failed run:

https://github.com/instinct-vfx/rez/actions/runs/14479919556/job/40614454516#step:6:35

Note that it failed on the "install rez with rez-pip" step not on the actual install. Does that not mean we need setuptools in rez's venv for good?

package_data={
'rez':
['utils/logging.conf'] +
Expand All @@ -93,8 +94,10 @@ def find_files(pattern, path=None, root="rez"):
"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.7,<3.14",
)
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
3 changes: 2 additions & 1 deletion src/rez/rezconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@

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! A change in python 3.12 does not allow single backslashes

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add r do the string like we usually do with regexes. THis will allow us to have a single \.

in docstrings. Sorry.) 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
62 changes: 23 additions & 39 deletions src/rez/vendor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,57 +39,51 @@ Our version is patched.
<tr><td>
atomicwrites
</td><td>
1.2.1 (Aug 30, 2018)
1.4.1 (Jul 8, 2022)
</td><td>
MIT
</td><td>
https://github.com/untitaker/python-atomicwrites
</td></tr>

<!-- ######################################################### -->
<tr><td>
attrs
</td><td>
19.1.0 (Mar 3, 2019)
</td><td>
MIT
</td><td>
https://github.com/python-attrs/attrs<br>
Added (July 2019) to enable the use of packaging lib that depends on it.
https://github.com/untitaker/python-atomicwrites<br>
No changes.<br>
Updated (April 2025) to help address py3.12 update.
</td></tr>

<!-- ######################################################### -->
<tr><td>
colorama
</td><td>
0.4.1 (Nov 25, 2018)
0.4.6 (Oct 24, 2022)
</td><td>
BSD 3-Clause
</td><td>
https://github.com/tartley/colorama<br>
No changes.<br>
Updated (April 2025) to help address py3.12 update.
</td></tr>

<!-- ######################################################### -->
<tr><td>
distlib
</td><td>
0.2.9.post0 (May 14, 2019)
0.3.9 (Oct 29, 2024)
</td><td>
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
</td><td>
https://bitbucket.org/pypa/distlib/src/master/<br>
Updated (June 2019) to enable wheel distribution based installations.
Updated (April 2025) to help address py3.12 update.
</td></tr>

<!-- ######################################################### -->
<tr><td>
distro
</td><td>
1.5.0 (Mar 31, 2020)
1.9.0 (Dec 24, 2023)
</td><td>
Apache 2.0
</td><td>
https://github.com/python-distro/distro
https://github.com/python-distro/distro<br>
No changes.<br>
Updated (April 2025) to help address py3.12 update.
</td></tr>

<!-- ######################################################### -->
Expand Down Expand Up @@ -161,12 +155,13 @@ https://github.com/pika/pika
<tr><td>
progress
</td><td>
1.5 (Mar 6, 2019)
1.6 (July 28, 2021)
</td><td>
ISC
</td><td>
https://github.com/verigak/progress<br>
Upgraded from 1.2 to 1.5 as of Oct 16 2019
No changes.<br>
Updated (April 2025) to help address py3.12 update.
</td></tr>

<!-- ######################################################### -->
Expand Down Expand Up @@ -227,37 +222,26 @@ Our version is patched.
<tr><td>
six
</td><td>
1.12.0 (Dec 9, 2018)
1.17.0 (Dec 4, 2024)
</td><td>
MIT
</td><td>
https://github.com/benjaminp/six<br>
Updated (July 2019) to coincide with packaging lib addition that depends on.
Also now required to support py2/3 interoperability.
</td></tr>

<!-- ######################################################### -->
<tr><td>
yaml lib (PyYAML)
</td><td>
5.1 (May 30, 2011)
</td><td>
MIT
</td><td>
https://github.com/yaml/pyyaml<br>
No changes but must maintain separate version between py2 and py3 for time being.
Updated (April 2025) to help address py3.12 update.<br>
No longer needed in rez itself, but still used by other vendored modules.
</td></tr>

<!-- ######################################################### -->
<tr><td>
yaml lib3 (PyYAML)
yaml (PyYAML)
</td><td>
5.1 (May 30, 2011)
6.0.1 (July 17, 2023)
</td><td>
MIT
</td><td>
https://github.com/yaml/pyyaml<br>
No changes but must maintain separate version between py2 and py3 for time being.
No changes. Bounded to 6.0.1 by current py3.7.<br>
Updated (April 2025) to help address py3.12 update.
</td></tr>

</table>
29 changes: 26 additions & 3 deletions src/rez/vendor/atomicwrites/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
except ImportError:
fcntl = None

__version__ = '1.2.1'
# `fspath` was added in Python 3.6
try:
from os import fspath
except ImportError:
fspath = None

__version__ = '1.4.1'


PY2 = sys.version_info[0] == 2
Expand Down Expand Up @@ -86,6 +92,7 @@ def replace_atomic(src, dst):
'''
Move ``src`` to ``dst``. If ``dst`` exists, it will be silently
overwritten.

Both paths must reside on the same filesystem for the operation to be
atomic.
'''
Expand All @@ -97,6 +104,7 @@ def move_atomic(src, dst):
Move ``src`` to ``dst``. There might a timewindow where both filesystem
entries exist. If ``dst`` already exists, :py:exc:`FileExistsError` will be
raised.

Both paths must reside on the same filesystem for the operation to be
atomic.
'''
Expand All @@ -106,14 +114,20 @@ def move_atomic(src, dst):
class AtomicWriter(object):
'''
A helper class for performing atomic writes. Usage::

with AtomicWriter(path).open() as f:
f.write(...)

:param path: The destination filepath. May or may not exist.
:param mode: The filemode for the temporary file. This defaults to `wb` in
Python 2 and `w` in Python 3.
:param overwrite: If set to false, an error is raised if ``path`` exists.
Errors are only raised after the file has been written to. Either way,
the operation is atomic.
:param open_kwargs: Keyword-arguments to pass to the underlying
:py:func:`open` call. This can be used to set the encoding when opening
files in text-mode.

If you need further control over the exact behavior, you are encouraged to
subclass.
'''
Expand All @@ -132,6 +146,10 @@ def __init__(self, path, mode=DEFAULT_MODE, overwrite=False,
if 'w' not in mode:
raise ValueError('AtomicWriters can only be written to.')

# Attempt to convert `path` to `str` or `bytes`
if fspath is not None:
path = fspath(path)

self._path = path
self._mode = mode
self._overwrite = overwrite
Expand Down Expand Up @@ -160,11 +178,13 @@ def _open(self, get_fileobject):
except Exception:
pass

def get_fileobject(self, dir=None, **kwargs):
def get_fileobject(self, suffix="", prefix=tempfile.gettempprefix(),
dir=None, **kwargs):
'''Return the temporary file to use.'''
if dir is None:
dir = os.path.normpath(os.path.dirname(self._path))
descriptor, name = tempfile.mkstemp(dir=dir)
descriptor, name = tempfile.mkstemp(suffix=suffix, prefix=prefix,
dir=dir)
# io.open() will take either the descriptor or the name, but we need
# the name later for commit()/replace_atomic() and couldn't find a way
# to get the filename from the descriptor.
Expand Down Expand Up @@ -194,12 +214,15 @@ def rollback(self, f):
def atomic_write(path, writer_cls=AtomicWriter, **cls_kwargs):
'''
Simple atomic writes. This wraps :py:class:`AtomicWriter`::

with atomic_write(path) as f:
f.write(...)

:param path: The target path to write to.
:param writer_cls: The writer class to use. This parameter is useful if you
subclassed :py:class:`AtomicWriter` to change some behavior and want to
use that new subclass.

Additional keyword arguments are passed to the writer class. See
:py:class:`AtomicWriter`.
'''
Expand Down
22 changes: 0 additions & 22 deletions src/rez/vendor/attr/LICENSE

This file was deleted.

65 changes: 0 additions & 65 deletions src/rez/vendor/attr/__init__.py

This file was deleted.

Loading
Loading