Skip to content

Commit

Permalink
Merge branch 'ephemeral-get-range' of https://github.com/davidlatwe/rez
Browse files Browse the repository at this point in the history
… into davidlatwe-ephemeral-get-range
  • Loading branch information
ajohns committed Mar 9, 2021
2 parents db64e18 + cb16644 commit ecc0922
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 6 deletions.
20 changes: 20 additions & 0 deletions src/rez/rex_bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@ def __init__(self, requirements):
def _attr_error(self, attr):
raise AttributeError("request does not exist: '%s'" % attr)

def get_range(self, name, default=None):
"""Returns requirement version range object"""
req_str = self._data.get(name)
if req_str:
return Requirement(req_str).range
elif default is not None:
return VersionRange(default)


class EphemeralsBinding(RO_MappingBinding):
"""Binds a list of resolved ephemeral packages.
Expand All @@ -193,6 +201,14 @@ def __init__(self, ephemerals):
def _attr_error(self, attr):
raise AttributeError("ephemeral does not exist: '%s'" % attr)

def get_range(self, name, default=None):
"""Returns ephemeral version range object"""
req_str = self._data.get(name)
if req_str:
return Requirement(req_str).range
elif default is not None:
return VersionRange(default)


def intersects(obj, range_):
"""Test if an object intersects with the given version range.
Expand Down Expand Up @@ -235,6 +251,10 @@ def commands():
return False
range2 = req.range

# eg 'if intersects(ephemerals.get_range('foo.cli', '1'), ...)'
elif isinstance(obj, VersionRange):
range2 = obj

# eg 'if intersects(resolve.maya, ...)'
elif isinstance(obj, VariantBinding):
range2 = VersionRange(str(obj.version))
Expand Down
107 changes: 106 additions & 1 deletion src/rez/tests/test_rex.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
from rez.rex import RexExecutor, Python, Setenv, Appendenv, Prependenv, Info, \
Comment, Alias, Command, Source, Error, Shebang, Unsetenv, expandable, \
literal
from rez.rex_bindings import VersionBinding
from rez.rex_bindings import VersionBinding, VariantsBinding, \
RequirementsBinding, EphemeralsBinding, intersects
from rez.exceptions import RexError, RexUndefinedVariableError
from rez.config import config
import unittest
from rez.vendor.version.version import Version
from rez.vendor.version.requirement import Requirement
from rez.tests.util import TestBase
from rez.utils.backcompat import convert_old_commands
from rez.package_repository import package_repository_manager
from rez.packages import iter_package_families
import inspect
import textwrap
import os
Expand Down Expand Up @@ -407,6 +411,107 @@ def test_old_style_commands(self):
annotate=False)
self.assertEqual(rez_commands, expected)

def test_intersects_resolve(self):
"""Test intersects with resolve object"""
resolved_pkg_data = {
"foo": {"1": {"name": "foo", "version": "1"}},
"maya": {"2020.1": {"name": "maya", "version": "2020.1"}},
}
mem_path = "memory@%s" % hex(id(resolved_pkg_data))
resolved_repo = package_repository_manager.get_repository(mem_path)
resolved_repo.data = resolved_pkg_data
resolved_packages = [
variant
for family in iter_package_families(paths=[mem_path])
for package in family.iter_packages()
for variant in package.iter_variants()
]
resolve = VariantsBinding(resolved_packages)
self.assertTrue(intersects(resolve.foo, "1"))
self.assertFalse(intersects(resolve.foo, "0"))
self.assertTrue(intersects(resolve.maya, "2019+"))
self.assertFalse(intersects(resolve.maya, "<=2019"))

def test_intersects_request(self):
"""Test intersects with request object"""
# request.get
request = RequirementsBinding([Requirement("foo.bar-1")])
bar_on = intersects(request.get("foo.bar", "0"), "1")
self.assertTrue(bar_on)

request = RequirementsBinding([])
bar_on = intersects(request.get("foo.bar", "0"), "1")
self.assertTrue(bar_on) # should be False, but for backward compat

request = RequirementsBinding([])
bar_on = intersects(request.get("foo.bar", "foo.bar-0"), "1")
self.assertFalse(bar_on) # workaround, see PR nerdvegas/rez#1030

# request.get_range
request = RequirementsBinding([Requirement("foo.bar-1")])
bar_on = intersects(request.get_range("foo.bar", "0"), "1")
self.assertTrue(bar_on)

request = RequirementsBinding([])
bar_on = intersects(request.get_range("foo.bar", "0"), "1")
self.assertFalse(bar_on)

request = RequirementsBinding([])
foo = intersects(request.get_range("foo", "==1.2.3"), "1.2")
self.assertTrue(foo)

request = RequirementsBinding([])
foo = intersects(request.get_range("foo", "==1.2.3"), "1.4")
self.assertFalse(foo)

request = RequirementsBinding([Requirement("foo-1.4.5")])
foo = intersects(request.get_range("foo", "==1.2.3"), "1.4")
self.assertTrue(foo)

def test_intersects_ephemerals(self):
"""Test intersects with ephemerals object"""
# ephemerals.get
ephemerals = EphemeralsBinding([Requirement(".foo.bar-1")])
bar_on = intersects(ephemerals.get("foo.bar", "0"), "1")
self.assertTrue(bar_on)

ephemerals = EphemeralsBinding([])
bar_on = intersects(ephemerals.get("foo.bar", "0"), "1")
self.assertTrue(bar_on) # should be False, but for backward compat

ephemerals = EphemeralsBinding([])
bar_on = intersects(ephemerals.get("foo.bar", "foo.bar-0"), "1")
self.assertFalse(bar_on) # workaround, see PR nerdvegas/rez#1030

ephemerals = EphemeralsBinding([])
self.assertRaises(RuntimeError, # no default
intersects, ephemerals.get("foo.bar"), "0")

# ephemerals.get_range
ephemerals = EphemeralsBinding([Requirement(".foo.bar-1")])
bar_on = intersects(ephemerals.get_range("foo.bar", "0"), "1")
self.assertTrue(bar_on)

ephemerals = EphemeralsBinding([])
bar_on = intersects(ephemerals.get_range("foo.bar", "0"), "1")
self.assertFalse(bar_on)

ephemerals = EphemeralsBinding([])
foo = intersects(ephemerals.get_range("foo", "==1.2.3"), "1.2")
self.assertTrue(foo)

ephemerals = EphemeralsBinding([])
foo = intersects(ephemerals.get_range("foo", "==1.2.3"), "1.4")
self.assertFalse(foo)

ephemerals = EphemeralsBinding([Requirement(".foo-1.4.5")])
foo = intersects(ephemerals.get_range("foo", "==1.2.3"), "1.4")
self.assertTrue(foo)

ephemerals = EphemeralsBinding([])
self.assertRaises(RuntimeError, # no default
intersects, ephemerals.get_range("foo.bar"), "0")


if __name__ == '__main__':
unittest.main()
Expand Down
11 changes: 8 additions & 3 deletions wiki/pages/Ephemeral-Packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,19 @@ to the [resolve](Package-Commands#resolve) object. You would typically use the

# in package.py
def commands()
if intersects(ephemerals.get('enable_tracking', '0'), '1'):
if intersects(ephemerals.get_range('enable_tracking', '0'), '1'):
env.TRACKING_ENABLED = 1

In this example, the given package would set the `TRACKING_ENABLED` environment
variable if an ephemeral such as `.enable_tracking-1` (or `.enable_tracking-1.2+`
etc) is present in the resolve. Note that the leading `.` is implied and not
included when querying the `ephemerals` object.

> [[media/icons/warning.png]] Since `ephemerals` is a dict-like object, so it has
> a `get` function which will return a full request string if key exists. Hence,
> the default value should also be a full request string, not just a version range
> string like `'0'` in `get_range`. Or `intersects` may not work as expect.
## Ephemeral Use Cases

Why would you want to request packages that don't exist? There are two main use
Expand All @@ -101,7 +106,7 @@ to packages in a resolve. For example, consider the following package definition
name = 'bah'

def commands():
if intersects(ephemerals.get('bah.cli', '1'), '1'):
if intersects(ephemerals.get_range('bah.cli', '1'), '1'):
env.PATH.append('{root}/bin')

This package will disable its command line tools if an ephemeral like `.bah.cli-0`
Expand All @@ -120,7 +125,7 @@ we introduce a `.cli` ephemeral that acts as a global whitelist:
name = 'bah'

def commands():
if intersects(ephemerals.get('cli', ''), 'bah'):
if intersects(ephemerals.get_range('cli', ''), 'bah'):
env.PATH.append('{root}/bin')

Here, all packages' cli will be enabled if `.cli` is not specified, but if it is
Expand Down
27 changes: 25 additions & 2 deletions wiki/pages/Package-Commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,14 +346,23 @@ like *env.append*, but prepends the environment variable instead.
### ephemerals
*Dict-like object*

if intersects(ephemerals.get("foo.cli", "1"), "1"):
env.PATH.append("{rot}/bin")
if intersects(ephemerals.get("foo.cli", default="foo.cli-1"), "1"):
env.PATH.append("{root}/bin")

A dict representing the list of ephemerals in the resolved environment. Each item is a
string (the full request, eg `.foo.cli-1`), keyed by the ephemeral package name. Note
that you do **not** include the leading `.` when getting items from the `ephemerals`
object.

Noted that the default value of `ephemerals.get` method should be a full request string so the `intersects`
can work as expect.

Alternatively, you can use `ephemerals.get_range` to get `VersionRange` object, which may provide
a bit intuitive way to interact with `intersects`.

if intersects(ephemerals.get_range("foo.cli", "1"), "1"):
env.PATH.append("{root}/bin")

### error
*Function*

Expand Down Expand Up @@ -433,6 +442,20 @@ This request would yield the following *request* object:
"corelib": "!corelib-1.4.4"
}

To use with `intersects`:

if intersects(request.get("foo.cli", default="foo.cli-1"), "1"):
env.PATH.append("{root}/bin")

Noted that the default value of `request.get` method should be a full request string so the `intersects`
can work as expect.

Alternatively, you can use `request.get_range` to get `VersionRange` object, which may provide
a bit intuitive way to interact with `intersects`.

if intersects(request.get_range("foo.cli", "1"), "1"):
env.PATH.append("{root}/bin")

> [[media/icons/info.png]] If multiple requests are present that refer to the same package, the
request is combined ahead of time. In other words, if requests *foo-4+* and *foo-<6* were both
present, the single request *foo-4+<6* would be present in the *request* object.
Expand Down

0 comments on commit ecc0922

Please sign in to comment.