Skip to content

Commit 5b4d6b8

Browse files
fixed uninstall, list validators cli cmds, and tests
1 parent b8d4d18 commit 5b4d6b8

File tree

6 files changed

+83
-110
lines changed

6 files changed

+83
-110
lines changed

guardrails/cli/hub/list.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import re
33

44
from guardrails.cli.hub.hub import hub_command
5-
from guardrails.cli.hub.utils import get_site_packages_location
65
from guardrails.hub_telemetry.hub_tracing import trace
76
from .console import console
87

@@ -11,7 +10,9 @@
1110
@trace(name="guardrails-cli/hub/list")
1211
def list():
1312
"""List all installed validators."""
14-
site_packages = get_site_packages_location()
13+
from guardrails.hub.validator_package_service import ValidatorPackageService
14+
15+
site_packages = ValidatorPackageService.get_site_packages_location()
1516
hub_init_file = os.path.join(site_packages, "guardrails", "hub", "__init__.py")
1617

1718
installed_validators = []

guardrails/cli/hub/uninstall.py

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import os
2-
import shutil
32
import sys
43
from typing import List, Literal
54

@@ -10,9 +9,7 @@
109
from guardrails.cli.server.hub_client import get_validator_manifest
1110
from guardrails_hub_types import Manifest
1211

13-
from guardrails.cli.hub.utils import get_site_packages_location
14-
from guardrails.cli.hub.utils import get_org_and_package_dirs
15-
from guardrails.cli.hub.utils import get_hub_directory
12+
from guardrails.cli.hub.utils import pip_process
1613
from guardrails.hub_telemetry.hub_tracing import trace
1714

1815
from .console import console
@@ -32,46 +29,28 @@ def remove_line(file_path: str, line_content: str):
3229

3330

3431
def remove_from_hub_inits(manifest: Manifest, site_packages: str):
35-
org_package = get_org_and_package_dirs(manifest)
32+
from guardrails.hub.validator_package_service import ValidatorPackageService
33+
3634
exports: List[str] = manifest.exports or []
3735
sorted_exports = sorted(exports, reverse=True)
38-
module_name = manifest.module_name
39-
relative_path = ".".join([*org_package, module_name])
40-
import_line = (
41-
f"from guardrails.hub.{relative_path} import {', '.join(sorted_exports)}"
36+
37+
validator_id = manifest.id
38+
import_path = ValidatorPackageService.get_import_path_from_validator_id(
39+
validator_id
4240
)
41+
import_line = f"from {import_path} import {', '.join(sorted_exports)}"
4342

4443
# Remove import line from main __init__.py
4544
hub_init_location = os.path.join(site_packages, "guardrails", "hub", "__init__.py")
4645
remove_line(hub_init_location, import_line)
4746

48-
# Remove import line from namespace __init__.py
49-
namespace = org_package[0]
50-
namespace_init_location = os.path.join(
51-
site_packages, "guardrails", "hub", namespace, "__init__.py"
52-
)
53-
lines = remove_line(namespace_init_location, import_line)
54-
55-
# remove namespace pkg if namespace __init__.py is empty
56-
if (len(lines) == 0) and (namespace != "hub"):
57-
logger.info(f"Removing namespace package {namespace} as it is now empty")
58-
try:
59-
shutil.rmtree(os.path.join(site_packages, "guardrails", "hub", namespace))
60-
except Exception as e:
61-
logger.error(f"Error removing namespace package {namespace}")
62-
logger.error(e)
63-
sys.exit(1)
64-
65-
66-
def uninstall_hub_module(manifest: Manifest, site_packages: str):
67-
uninstall_directory = get_hub_directory(manifest, site_packages)
68-
logger.info(f"Removing directory {uninstall_directory}")
69-
try:
70-
shutil.rmtree(uninstall_directory)
71-
except Exception as e:
72-
logger.error("Error removing directory")
73-
logger.error(e)
74-
sys.exit(1)
47+
48+
def uninstall_hub_module(manifest: Manifest):
49+
from guardrails.hub.validator_package_service import ValidatorPackageService
50+
51+
validator_id = manifest.id
52+
package_name = ValidatorPackageService.get_normalized_package_name(validator_id)
53+
pip_process("uninstall", package_name, flags=["-y"], quiet=True)
7554

7655

7756
@hub_command.command()
@@ -82,6 +61,9 @@ def uninstall(
8261
),
8362
):
8463
"""Uninstall a validator from the Hub."""
64+
from guardrails.hub.validator_package_service import ValidatorPackageService
65+
66+
print(f"ValidatorPackageService: {ValidatorPackageService}")
8567
if not package_uri.startswith("hub://"):
8668
logger.error("Invalid URI!")
8769
sys.exit(1)
@@ -98,11 +80,11 @@ def uninstall(
9880
# Prep
9981
with console.status("Fetching manifest", spinner="bouncingBar"):
10082
module_manifest = get_validator_manifest(module_name)
101-
site_packages = get_site_packages_location()
83+
site_packages = ValidatorPackageService.get_site_packages_location()
10284

10385
# Uninstall
10486
with console.status("Removing module", spinner="bouncingBar"):
105-
uninstall_hub_module(module_manifest, site_packages)
87+
uninstall_hub_module(module_manifest)
10688

10789
# Cleanup
10890
with console.status("Cleaning up", spinner="bouncingBar"):

guardrails/cli/hub/utils.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def pip_process(
5858
except Exception:
5959
logger.debug(
6060
f"JSON parse exception in decoding output from pip {action}"
61-
f"{package}. Falling back to accumulating the byte stream",
61+
f" {package}. Falling back to accumulating the byte stream",
6262
)
6363
accumulator = {}
6464
parsed = BytesHeaderParser().parsebytes(result.stdout.encode())
@@ -77,20 +77,13 @@ def pip_process(
7777
f"stdout: {(exc.stdout or '').strip()}"
7878
)
7979
)
80-
# Re-raise the error or handle it accordingly
81-
raise
80+
sys.exit(1)
8281
except Exception as e:
8382
logger.error(
8483
f"An unexpected exception occurred while trying to {action} {package}!",
8584
e,
8685
)
87-
raise
88-
89-
90-
def get_site_packages_location() -> str:
91-
output = pip_process("show", "pip", format=json_format)
92-
pip_location = output["Location"] # type: ignore
93-
return pip_location
86+
sys.exit(1)
9487

9588

9689
def get_org_and_package_dirs(manifest: Manifest) -> List[str]:

tests/unit_tests/cli/hub/test_install.py

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from unittest.mock import ANY, call
1+
from unittest.mock import ANY, MagicMock, call
22
from typer.testing import CliRunner
33
from guardrails.cli.hub.install import hub_command
44

@@ -140,25 +140,27 @@ def test_no_package_string_format(self, mocker):
140140

141141
mock_sys_executable = mocker.patch("guardrails.cli.hub.utils.sys.executable")
142142

143-
mock_subprocess_check_output = mocker.patch(
144-
"guardrails.cli.hub.utils.subprocess.check_output"
145-
)
146-
mock_subprocess_check_output.return_value = str.encode("string output")
143+
mock_subprocess_run = mocker.patch("guardrails.cli.hub.utils.subprocess.run")
144+
subprocess_result_mock = MagicMock()
145+
subprocess_result_mock.stdout = "string output"
146+
mock_subprocess_run.return_value = subprocess_result_mock
147147

148148
from guardrails.cli.hub.utils import pip_process
149149

150150
response = pip_process("inspect", flags=["--path=./install-here"])
151151

152-
assert mock_logger_debug.call_count == 2
152+
assert mock_logger_debug.call_count == 1
153153
debug_calls = [
154154
call("running pip inspect --path=./install-here "),
155-
call("decoding output from pip inspect "),
156155
]
157156
mock_logger_debug.assert_has_calls(debug_calls)
158157

159-
mock_subprocess_check_output.assert_called_once_with(
158+
mock_subprocess_run.assert_called_once_with(
160159
[mock_sys_executable, "-m", "pip", "inspect", "--path=./install-here"],
161160
env={},
161+
capture_output=True,
162+
text=True,
163+
check=True,
162164
)
163165

164166
assert response == "string output"
@@ -169,10 +171,11 @@ def test_json_format(self, mocker):
169171

170172
mock_sys_executable = mocker.patch("guardrails.cli.hub.utils.sys.executable")
171173

172-
mock_subprocess_check_output = mocker.patch(
173-
"guardrails.cli.hub.utils.subprocess.check_output"
174-
)
175-
mock_subprocess_check_output.return_value = str.encode("json output")
174+
mock_subprocess_run = mocker.patch("guardrails.cli.hub.utils.subprocess.run")
175+
subprocess_result_mock = MagicMock()
176+
subprocess_result_mock.stdout = "json outout"
177+
178+
mock_subprocess_run.return_value = subprocess_result_mock
176179

177180
class MockBytesHeaderParser:
178181
def parsebytes(self, *args):
@@ -186,18 +189,21 @@ def parsebytes(self, *args):
186189

187190
response = pip_process("show", "pip", format="json")
188191

189-
assert mock_logger_debug.call_count == 3
192+
assert mock_logger_debug.call_count == 2
190193
debug_calls = [
191194
call("running pip show pip"),
192-
call("decoding output from pip show pip"),
193195
call(
194196
"JSON parse exception in decoding output from pip show pip. Falling back to accumulating the byte stream" # noqa
195197
),
196198
]
197199
mock_logger_debug.assert_has_calls(debug_calls)
198200

199-
mock_subprocess_check_output.assert_called_once_with(
200-
[mock_sys_executable, "-m", "pip", "show", "pip"], env={}
201+
mock_subprocess_run.assert_called_once_with(
202+
[mock_sys_executable, "-m", "pip", "show", "pip"],
203+
env={},
204+
capture_output=True,
205+
text=True,
206+
check=True,
201207
)
202208

203209
assert response == {"output": "json"}
@@ -271,16 +277,3 @@ def test_install_with_upgrade_flag(self, mocker):
271277
)
272278

273279
assert result.exit_code == 0
274-
275-
276-
def test_get_site_packages_location(mocker):
277-
mock_pip_process = mocker.patch("guardrails.cli.hub.utils.pip_process")
278-
mock_pip_process.return_value = {"Location": "/site-packages"}
279-
280-
from guardrails.cli.hub.utils import get_site_packages_location
281-
282-
response = get_site_packages_location()
283-
284-
mock_pip_process.assert_called_once_with("show", "pip", format="json")
285-
286-
assert response == "/site-packages"

tests/unit_tests/cli/hub/test_list.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class TestListCommand:
1313
@pytest.fixture(autouse=True)
1414
def setup(self, mocker):
1515
mocker.patch(
16-
"guardrails.cli.hub.utils.get_site_packages_location",
16+
"guardrails.hub.validator_package_service.ValidatorPackageService.get_manifest_and_site_packages",
1717
return_value="/test/site-packages",
1818
)
1919

tests/unit_tests/cli/hub/test_uninstall.py

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,35 @@
55
from guardrails_hub_types import Manifest
66
from guardrails.cli.hub.uninstall import remove_from_hub_inits
77

8-
manifest_mock = Manifest(
9-
encoder="some_encoder",
10-
id="module_id",
11-
name="test_module",
12-
author={"name": "Author Name", "email": "[email protected]"},
13-
maintainers=[{"name": "Maintainer Name", "email": "[email protected]"}],
14-
repository={"url": "https://github.com/example/repo"},
15-
namespace="guardrails",
16-
package_name="test_package",
17-
description="Test module",
18-
module_name="test_module",
19-
exports=["Validator", "Helper"],
8+
manifest_mock = Manifest.from_dict(
9+
{
10+
"id": "guardrails/test_package",
11+
"name": "test_module",
12+
"author": {"name": "Author Name", "email": "[email protected]"},
13+
"maintainers": [{"name": "Maintainer Name", "email": "[email protected]"}],
14+
"repository": {"url": "https://github.com/example/repo"},
15+
"packageName": "test_package",
16+
"moduleName": "test_module",
17+
"namespace": "guardrails",
18+
"description": "Test module",
19+
"exports": ["Validator", "Helper"],
20+
}
2021
)
2122

2223

2324
def test_remove_from_hub_inits(mocker):
24-
mocker.patch(
25-
"guardrails.cli.hub.uninstall.get_org_and_package_dirs",
26-
return_value=["guardrails", "test_package"],
27-
)
2825
mock_remove_line = mocker.patch("guardrails.cli.hub.uninstall.remove_line")
29-
mock_remove_dirs = mocker.patch("shutil.rmtree")
3026

3127
remove_from_hub_inits(manifest_mock, "/site-packages")
3228

3329
expected_calls = [
3430
call(
3531
"/site-packages/guardrails/hub/__init__.py",
36-
"from guardrails.hub.guardrails.test_package.test_module import "
37-
"Validator, Helper",
38-
),
39-
call(
40-
"/site-packages/guardrails/hub/guardrails/__init__.py",
41-
"from guardrails.hub.guardrails.test_package.test_module import "
42-
"Validator, Helper",
32+
"from guardrails_grhub_test_package import " "Validator, Helper",
4333
),
4434
]
4535

4636
mock_remove_line.assert_has_calls(expected_calls, any_order=True)
47-
mock_remove_dirs.assert_called_once_with("/site-packages/guardrails/hub/guardrails")
4837

4938

5039
def test_uninstall_invalid_uri(mocker):
@@ -81,10 +70,7 @@ def test_uninstall_valid_uri(mocker):
8170
"guardrails.cli.hub.uninstall.get_validator_manifest",
8271
return_value=manifest_mock,
8372
)
84-
mocker.patch(
85-
"guardrails.cli.hub.uninstall.get_site_packages_location",
86-
return_value="/site-packages",
87-
)
73+
8874
mock_uninstall_hub_module = mocker.patch(
8975
"guardrails.cli.hub.uninstall.uninstall_hub_module"
9076
)
@@ -93,9 +79,27 @@ def test_uninstall_valid_uri(mocker):
9379
)
9480
mocker.patch("guardrails.cli.hub.uninstall.console")
9581

82+
validator_package_service_mock = mocker.patch(
83+
"guardrails.hub.validator_package_service.ValidatorPackageService",
84+
)
85+
validator_package_service_mock.get_site_packages_location.return_value = (
86+
"/site-packages"
87+
)
88+
9689
from guardrails.cli.hub.uninstall import uninstall
9790

9891
uninstall("hub://guardrails/test-validator")
9992

100-
mock_uninstall_hub_module.assert_called_once_with(manifest_mock, "/site-packages")
93+
mock_uninstall_hub_module.assert_called_once_with(manifest_mock)
10194
mock_remove_from_hub_inits.assert_called_once_with(manifest_mock, "/site-packages")
95+
96+
97+
def test_uninstall_hub_module(mocker):
98+
mock_pip_process = mocker.patch("guardrails.cli.hub.uninstall.pip_process")
99+
from guardrails.cli.hub.uninstall import uninstall_hub_module
100+
101+
uninstall_hub_module(manifest_mock)
102+
103+
mock_pip_process.assert_called_once_with(
104+
"uninstall", "guardrails-grhub-test-package", flags=["-y"], quiet=True
105+
)

0 commit comments

Comments
 (0)