Skip to content

Commit de43a01

Browse files
Merge pull request #1156 from guardrails-ai/0.6.0-dev-validator-extras
0.6.0 dev validator extras
2 parents d8fd51a + 943d0c7 commit de43a01

File tree

2 files changed

+120
-7
lines changed

2 files changed

+120
-7
lines changed

guardrails/cli/hub/utils.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,87 @@
2020
string_format = "string"
2121

2222

23+
class PipProcessError(Exception):
24+
action: str
25+
package: str
26+
stderr: str = ""
27+
stdout: str = ""
28+
returncode: int = 1
29+
30+
def __init__(
31+
self,
32+
action: str,
33+
package: str,
34+
stderr: str = "",
35+
stdout: str = "",
36+
returncode: int = 1,
37+
):
38+
self.action = action
39+
self.package = package
40+
self.stderr = stderr
41+
self.stdout = stdout
42+
self.returncode = returncode
43+
message = (
44+
f"PipProcessError: {action} on '{package}' failed with"
45+
"return code {returncode}.\n"
46+
f"Stdout:\n{stdout}\n"
47+
f"Stderr:\n{stderr}"
48+
)
49+
super().__init__(message)
50+
51+
52+
def pip_process_with_custom_exception(
53+
action: str,
54+
package: str = "",
55+
flags: List[str] = [],
56+
format: Union[Literal["string"], Literal["json"]] = string_format,
57+
quiet: bool = False,
58+
no_color: bool = False,
59+
) -> Union[str, dict]:
60+
try:
61+
if not quiet:
62+
logger.debug(f"running pip {action} {' '.join(flags)} {package}")
63+
command = [sys.executable, "-m", "pip", action]
64+
command.extend(flags)
65+
if package:
66+
command.append(package)
67+
68+
env = dict(os.environ)
69+
if no_color:
70+
env["NO_COLOR"] = "true"
71+
72+
result = subprocess.run(
73+
command,
74+
env=env,
75+
capture_output=True, # Capture both stdout and stderr
76+
text=True, # Automatically decode to strings
77+
check=True, # Automatically raise error on non-zero exit code
78+
)
79+
80+
if format == json_format:
81+
try:
82+
remove_color_codes = re.compile(r"\x1b\[[0-9;]*m")
83+
parsed_as_string = re.sub(remove_color_codes, "", result.stdout.strip())
84+
return json.loads(parsed_as_string)
85+
except Exception:
86+
logger.debug(
87+
f"JSON parse exception in decoding output from pip {action}"
88+
f" {package}. Falling back to accumulating the byte stream",
89+
)
90+
accumulator = {}
91+
parsed = BytesHeaderParser().parsebytes(result.stdout.encode())
92+
for key, value in parsed.items():
93+
accumulator[key] = value
94+
return accumulator
95+
96+
return result.stdout
97+
98+
except subprocess.CalledProcessError as exc:
99+
raise PipProcessError(action, package, exc.stderr, exc.stdout, exc.returncode)
100+
except Exception as e:
101+
raise PipProcessError(action, package, stderr=str(e), stdout="", returncode=1)
102+
103+
23104
def pip_process(
24105
action: str,
25106
package: str = "",

guardrails/hub/validator_package_service.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from guardrails.logger import logger as guardrails_logger
1313

1414

15-
from guardrails.cli.hub.utils import pip_process
15+
from guardrails.cli.hub.utils import PipProcessError, pip_process_with_custom_exception
1616
from guardrails_hub_types import Manifest
1717
from guardrails.cli.server.hub_client import get_validator_manifest
1818
from guardrails.settings import settings
@@ -251,7 +251,6 @@ def install_hub_module(
251251
validator_id
252252
)
253253
validator_version = validator_version if validator_version else ""
254-
full_package_name = f"{pep_503_package_name}{validator_version}"
255254

256255
guardrails_token = settings.rc.token
257256

@@ -267,8 +266,41 @@ def install_hub_module(
267266
pip_flags.append("-q")
268267

269268
# Install from guardrails hub pypi server with public pypi index as fallback
270-
download_output = pip_process(
271-
"install", full_package_name, pip_flags, quiet=quiet
272-
)
273-
if not quiet:
274-
logger.info(download_output)
269+
270+
try:
271+
full_package_name = f"{pep_503_package_name}[validators]{validator_version}"
272+
download_output = pip_process_with_custom_exception(
273+
"install", full_package_name, pip_flags, quiet=quiet
274+
)
275+
if not quiet:
276+
logger.info(download_output)
277+
except PipProcessError:
278+
try:
279+
full_package_name = f"{pep_503_package_name}{validator_version}"
280+
download_output = pip_process_with_custom_exception(
281+
"install", full_package_name, pip_flags, quiet=quiet
282+
)
283+
if not quiet:
284+
logger.info(download_output)
285+
except PipProcessError as e:
286+
action = e.action
287+
package = e.package
288+
stderr = e.stderr
289+
stdout = e.stdout
290+
returncode = e.returncode
291+
logger.error(
292+
(
293+
f"Failed to {action} {package}\n"
294+
f"Exit code: {returncode}\n"
295+
f"stderr: {(stderr or '').strip()}\n"
296+
f"stdout: {(stdout or '').strip()}"
297+
)
298+
)
299+
raise
300+
except Exception as e:
301+
logger.error(
302+
"An unexpected exception occurred while "
303+
f"installing {validator_id}: ",
304+
e,
305+
)
306+
raise

0 commit comments

Comments
 (0)