Repository: nerdvegas/rez
Pull request #886 (2020-05-09 01:57:42)

added get_variant_from_uri functionality
This is a first step towards https://github.com/nerdvegas/rez/issues/680, which I'm starting to work on now (yay!). The intent here is that you should be able to munge commands together to do useful things fairly easily.

For example, to warm the package cache, you might do this:

```
> ]$ rez-context --resolve --show-uris | xargs rez-package-cache --add-variant
```

In a similar vein, perhaps you want to create a local package repo for a context, so you can create a new context using only these variants, and then copy the whole thing to a server (more work is required to make this fully standalone, but I'm keen to see this feature, as is our engineering department, who hate nfs mounts :) ):

```
> ]$ rez-context --resolve --show-uris | xargs -i rez-cp --variant-uris {} --dest-path /mylocalrepo
```



Patch:
diff --git a/src/rez/package_repository.py b/src/rez/package_repository.py
index b4a59526..19b0cee8 100644
--- a/src/rez/package_repository.py
+++ b/src/rez/package_repository.py
@@ -170,6 +170,18 @@ class PackageRepository(object):
         """
         raise NotImplementedError
 
+    def get_variant_from_uri(self, uri):
+        """Get a variant given its URI.
+
+        Args:
+            uri (str): Variant URI
+
+        Returns:
+            `VariantResource`, or None if the variant is not present in this
+            package repository.
+        """
+        return None
+
     def pre_variant_install(self, variant_resource):
         """Called before a variant is installed.
 
diff --git a/src/rez/packages.py b/src/rez/packages.py
index 8a34ac42..1ff1b9ab 100644
--- a/src/rez/packages.py
+++ b/src/rez/packages.py
@@ -637,12 +637,38 @@ def get_variant(variant_handle, context=None):
     return variant
 
 
+def get_variant_from_uri(uri, paths=None):
+    """Get a variant given its URI.
+
+    Args:
+        uri (str): Variant URI
+        paths (list of str): paths to search for variants, defaults to
+            `config.packages_path`.
+
+    Returns:
+        `VariantResource`, or None if the variant is not present in this
+        package repository.
+    """
+    for path in (paths or config.packages_path):
+        repo = package_repository_manager.get_repository(path)
+        variant_resource = repo.get_variant_from_uri(uri)
+        if variant_resource is not None:
+            return Variant(variant_resource)
+
+    return None
+
+
 def get_last_release_time(name, paths=None):
     """Returns the most recent time this package was released.
 
     Note that releasing a variant into an already-released package is also
     considered a package release.
 
+    Args:
+        name (str): Package family name.
+        paths (list of str): paths to search for packages, defaults to
+            `config.packages_path`.
+
     Returns:
         int: Epoch time of last package release, or zero if this cannot be
         determined.
diff --git a/src/rez/utils/_version.py b/src/rez/utils/_version.py
index 6525d50d..96115da4 100644
--- a/src/rez/utils/_version.py
+++ b/src/rez/utils/_version.py
@@ -1,7 +1,7 @@
 
 
 # Update this value to version up Rez. Do not place anything else in this file.
-_rez_version = "2.59.0"
+_rez_version = "2.60.0"
 
 
 # Copyright 2013-2016 Allan Johns.
diff --git a/src/rezplugins/package_repository/filesystem.py b/src/rezplugins/package_repository/filesystem.py
index 8edc4906..2f23cfda 100644
--- a/src/rezplugins/package_repository/filesystem.py
+++ b/src/rezplugins/package_repository/filesystem.py
@@ -534,6 +534,61 @@ class FileSystemPackageRepository(PackageRepository):
     def get_last_release_time(self, package_family_resource):
         return package_family_resource.get_last_release_time()
 
+    def get_variant_from_uri(self, uri):
+        """
+        Example URIs:
+        - /svr/packages/mypkg/1.0.0/package.py[1]
+        - /svr/packages/mypkg/1.0.0/package.py[]  ("null" variant)
+        - /svr/packages/mypkg/package.py[1]  (unversioned package - rare)
+        """
+        i = uri.rfind('[')
+        if i == -1:
+            return None
+
+        prefix = self.location + os.path.sep
+        if not uri.startswith(prefix):
+            return None
+
+        part1 = uri[len(prefix):i]  # 'mypkg/1.0.0/package.py'
+        part2 = uri[i:][1:-1]  # the '1' in '[1]'
+
+        # find package
+        parts = part1.split(os.path.sep)
+        if len(parts) == 3:  # versioned package
+            pkg_name, pkg_ver_str = parts[0], parts[1]
+        elif len(parts) == 2:  # unversioned package
+            pkg_name, pkg_ver_str = parts[0], ''
+        else:
+            return None
+
+        fam = self.get_package_family(pkg_name)
+        if fam is None:
+            return None
+
+        ver = Version(pkg_ver_str)
+        pkg = None
+
+        for package in fam.iter_packages():
+            if package.version == ver:
+                pkg = package
+                break
+
+        if pkg is None:
+            return None
+
+        # find variant in package
+        try:
+            variant_index = int(part2)
+        except:
+            # future proof - we may move to hash-based indices for hashed variants
+            variant_index = part2
+
+        for variant in pkg.iter_variants():
+            if variant.index == variant_index:
+                return variant
+
+        return None
+
     @cached_property
     def file_lock_dir(self):
         dirname = _settings.file_lock_dir


Test patch:
diff --git a/src/rez/tests/test_packages.py b/src/rez/tests/test_packages.py
index 3405fdec..576572d8 100644
--- a/src/rez/tests/test_packages.py
+++ b/src/rez/tests/test_packages.py
@@ -2,7 +2,7 @@
 test package iteration, serialization etc
 """
 from rez.packages import iter_package_families, iter_packages, get_package, \
-    create_package, get_developer_package
+    create_package, get_developer_package, get_variant_from_uri
 from rez.package_py_utils import expand_requirement
 from rez.package_resources import package_release_keys
 from rez.tests.util import TestBase, TempdirMixin
@@ -422,6 +422,13 @@ class TestPackages(TestBase, TempdirMixin):
         for req in bad_tests:
             self.assertRaises(VersionError, expand_requirement, req)
 
+    def test_variant_from_uri(self):
+        """Test getting a variant from its uri."""
+        package = get_package("variants_py", "2.0")
+        for variant in package.iter_variants():
+            variant2 = get_variant_from_uri(variant.uri)
+            self.assertEqual(variant, variant2)
+
 
 class TestMemoryPackages(TestBase):
     def test_1_memory_variant_parent(self):