Skip to content
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

package removal #1063

Merged
merged 5 commits into from
Apr 16, 2021
Merged
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
9 changes: 8 additions & 1 deletion src/rez/cli/_entry_points.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,14 @@ def run_rez_pkg_ignore():


@scriptname("rez-mv")
def run_rez_pkg_mv():
def run_rez_mv():
check_production_install()
from rez.cli._main import run
return run("mv")


@scriptname("rez-rm")
def run_rez_rm():
check_production_install()
from rez.cli._main import run
return run("rm")
3 changes: 2 additions & 1 deletion src/rez/cli/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
"bundle": {},
"benchmark": {},
"pkg-ignore": {},
"mv": {}
"mv": {},
"rm": {}
}


Expand Down
4 changes: 2 additions & 2 deletions src/rez/cli/mv.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

def setup_parser(parser, completions=False):
parser.add_argument(
"--dest-path", metavar="PATH", required=True,
"-d", "--dest-path", metavar="PATH", required=True,
help="package repository to move PKG to.")
parser.add_argument(
"-k", "--keep-timestamp", action="store_true",
Expand All @@ -16,7 +16,7 @@ def setup_parser(parser, completions=False):
help="move package even if it isn't relocatable (use at your own risk)")
pkg_action = parser.add_argument(
"PKG",
help="package to move")
help="package to move (eg 'foo-1.2.3')")
parser.add_argument(
"PATH", nargs='?',
help="The repository containing the package. If not specified, this "
Expand Down
75 changes: 75 additions & 0 deletions src/rez/cli/rm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'''
Remove package(s) from a repository.
'''
from __future__ import print_function
import sys


def setup_parser(parser, completions=False):
group = parser.add_mutually_exclusive_group()
group.add_argument(
"-p", "--package",
help="remove the specified package (eg 'foo-1.2.3'). This will work "
"even if the package is currently ignored.")
group.add_argument(
"-i", "--ignored-since", type=int, metavar="DAYS",
help="remove all packages that have been ignored for >= DAYS")

parser.add_argument(
"--dry-run", action="store_true",
help="dry run mode")
parser.add_argument(
"PATH", nargs='?',
help="the repository containing the package(s) to remove.")


def remove_package(opts, parser):
from rez.vendor.version.requirement import VersionedObject
from rez.package_remove import remove_package

if opts.dry_run:
parser.error("--dry-run is not supported with --package")

if not opts.PATH:
parser.error("Must specify PATH with --package")

obj = VersionedObject(opts.package)

if remove_package(obj.name, obj.version, opts.PATH):
print("Package removed.")
else:
print("Package not found.", file=sys.stderr)
sys.exit(1)


def remove_ignored_since(opts, parser):
from rez.package_remove import remove_packages_ignored_since

if opts.PATH:
paths = [opts.PATH]
else:
paths = None

num_removed = remove_packages_ignored_since(
days=opts.ignored_since,
paths=paths,
dry_run=opts.dry_run,
verbose=opts.verbose
)

if num_removed:
if opts.dry_run:
print("%d packages would be removed." % num_removed)
else:
print("%d packages were removed." % num_removed)
else:
print("No packages were removed.")


def command(opts, parser, extra_arg_groups=None):
if opts.package:
remove_package(opts, parser)
elif opts.ignored_since is not None:
remove_ignored_since(opts, parser)
else:
parser.error("Must specify either --package or --ignored-since")
61 changes: 61 additions & 0 deletions src/rez/package_remove.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from rez.package_repository import package_repository_manager
from rez.vendor.version.version import Version
from rez.utils.logging_ import print_info
from rez.vendor.six import six
from rez.config import config


basestring = six.string_types[0]


def remove_package(name, version, path):
"""Remove a package from its repository.

Note that you are able to remove a package that is hidden (ie ignored).
This is why a Package instance is not specified (if the package were hidden,
you wouldn't be able to get one).

Args:
name (str): Name of package.
version (Version or str): Version of the package, eg '1.0.0'
path (str): Package repository path containing the package.

Returns:
bool: True if the package was removed, False if package not found.
"""
if isinstance(version, basestring):
version = Version(version)

repo = package_repository_manager.get_repository(path)
return repo.remove_package(name, version)


def remove_packages_ignored_since(days, paths=None, dry_run=False, verbose=False):
"""Remove packages ignored for >= specified number of days.

Args:
days (int): Remove packages ignored >= this many days
paths (list of str, optional): Paths to search for packages, defaults
to `config.packages_path`.
dry_run: Dry run mode
verbose (bool): Verbose mode

Returns:
int: Number of packages removed. In dry-run mode, returns the number of
packages that _would_ be removed.
"""
num_removed = 0

for path in (paths or config.packages_path):
repo = package_repository_manager.get_repository(path)

if verbose:
print_info("Searching %s...", repo)

num_removed += repo.remove_ignored_since(
days=days,
dry_run=dry_run,
verbose=verbose
)

return num_removed
29 changes: 29 additions & 0 deletions src/rez/package_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,35 @@ def unignore_package(self, pkg_name, pkg_version):
"""
raise NotImplementedError

def remove_package(self, pkg_name, pkg_version):
"""Remove a package.

Note that this should work even if the specified package is currently
ignored.

Args:
pkg_name (str): Package name
pkg_version(`Version`): Package version

Returns:
bool: True if the package was removed, False if it wasn't found.
"""
raise NotImplementedError

def remove_ignored_since(self, days, dry_run=False, verbose=False):
"""Remove packages ignored for >= specified number of days.

Args:
days (int): Remove packages ignored >= this many days
dry_run: Dry run mode
verbose (bool): Verbose mode

Returns:
int: Number of packages removed. In dry-run mode, returns the
number of packages that _would_ be removed.
"""
raise NotImplementedError

def pre_variant_install(self, variant_resource):
"""Called before a variant is installed.

Expand Down
9 changes: 6 additions & 3 deletions src/rez/packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from rez.utils.schema import schema_keys
from rez.utils.resources import ResourceHandle, ResourceWrapper
from rez.exceptions import PackageFamilyNotFoundError, ResourceError
from rez.vendor.version.version import VersionRange
from rez.vendor.version.version import Version, VersionRange
from rez.vendor.version.requirement import VersionedObject
from rez.vendor.six import six
from rez.serialise import FileFormat
Expand Down Expand Up @@ -603,6 +603,9 @@ def get_package_from_repository(name, version, path):
"""
repo = package_repository_manager.get_repository(path)

if isinstance(version, basestring):
version = Version(version)

package_resource = repo.get_package(name, version)
if package_resource is None:
return None
Expand Down Expand Up @@ -905,8 +908,8 @@ def get_latest_package_from_string(txt, paths=None, error=False):

Args:
txt (str): Request, eg 'foo-1.2+'
paths (list of str, optional): paths to search for package families,
defaults to `config.packages_path`.
paths (list of str, optional): paths to search for packages, defaults
to `config.packages_path`.
error (bool): If True, raise an error if no package is found.

Returns:
Expand Down
57 changes: 57 additions & 0 deletions src/rez/tests/test_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from rez.package_py_utils import expand_requirement
from rez.package_resources import package_release_keys
from rez.package_move import move_package
from rez.package_remove import remove_package, remove_packages_ignored_since
from rez.package_repository import package_repository_manager
from rez.tests.util import TestBase, TempdirMixin
from rez.utils.formatting import PackageRequest
from rez.utils.sourcecode import SourceCode
Expand Down Expand Up @@ -452,10 +454,18 @@ def test_package_ignore(self):

repo = pkg.repository

# ignore a pkg that doesn't exist
i = repo.ignore_package("i_dont_exist", Version("1"))
self.assertEqual(i, -1)

# ignore a pkg that doesn't exist, but allow it
i = repo.ignore_package("i_dont_exist", Version("1"), allow_missing=True)
self.assertEqual(i, 1)

# unignore a pkg that doesn't exist
i = repo.unignore_package("i_dont_exist", Version("1"))
self.assertEqual(i, -1)

# ignore an existing package
i = repo.ignore_package(pkg_name, pkg_version)
self.assertEqual(i, 1)
Expand Down Expand Up @@ -508,6 +518,53 @@ def test_package_move(self):
src_pkg = get_package_from_repository(pkg_name, pkg_version, repo_path)
self.assertEqual(src_pkg, None)

def test_package_remove(self):
"""Test package remove."""
pkg_name = "pydad"
pkg_version = Version("2")

# copy packages to a temp repo
repo_path = os.path.join(self.root, "tmp4_packages")
shutil.copytree(self.solver_packages_path, repo_path)

# verify that source pkg exists
src_pkg = get_package_from_repository(pkg_name, pkg_version, repo_path)
self.assertNotEqual(src_pkg, None)

# remove it
was_removed = remove_package(pkg_name, pkg_version, repo_path)
self.assertTrue(was_removed)

# verify that source pkg no longer exists (and isn't simply ignored)
repo = package_repository_manager.get_repository(repo_path)
i = repo.unignore_package(pkg_name, pkg_version)
self.assertEqual(i, -1)

def test_remove_packages_ignored_since(self):
pkg_name = "pydad"
pkg_version = Version("2")

# copy packages to a temp repo
repo_path = os.path.join(self.root, "tmp5_packages")
shutil.copytree(self.solver_packages_path, repo_path)

# verify that source pkg exists
src_pkg = get_package_from_repository(pkg_name, pkg_version, repo_path)
self.assertNotEqual(src_pkg, None)

# ignore it
repo = package_repository_manager.get_repository(repo_path)
i = repo.ignore_package(pkg_name, pkg_version)
self.assertEqual(i, 1)

# remove all ignored packages
num_removed = remove_packages_ignored_since(days=0, paths=[repo_path])
self.assertEqual(num_removed, 1)

# verify that source pkg no longer exists (and isn't simply ignored)
i = repo.unignore_package(pkg_name, pkg_version)
self.assertEqual(i, -1)


class TestMemoryPackages(TestBase):
def test_1_memory_variant_parent(self):
Expand Down
Loading