11import  importlib 
22import  os 
33from  pathlib  import  Path 
4+ import  re 
45import  subprocess 
56import  sys 
67
7- from  typing  import  List , Literal 
8+ from  typing  import  List , Literal ,  Optional 
89from  types  import  ModuleType 
910from  pydash .strings  import  snake_case 
11+ from  packaging .utils  import  canonicalize_name   # PEP 503 
1012
11- from  guardrails .classes .generic .stack  import  Stack 
1213from  guardrails .logger  import  logger  as  guardrails_logger 
1314
1415
1516from  guardrails .cli .hub .utils  import  pip_process 
1617from  guardrails_hub_types  import  Manifest 
1718from  guardrails .cli .server .hub_client  import  get_validator_manifest 
19+ from  guardrails .settings  import  settings 
1820
1921
2022json_format : Literal ["json" ] =  "json" 
@@ -91,11 +93,13 @@ def get_validator_from_manifest(manifest: Manifest) -> ModuleType:
9193        Returns: 
9294            Any: The Validator class from the installed module 
9395        """ 
94-         org_package  =  ValidatorPackageService .get_org_and_package_dirs (manifest )
95-         module_name  =  manifest .module_name 
9696
97-         _relative_path  =  "." .join ([* org_package , module_name ])
98-         import_line  =  f"guardrails.hub.{ _relative_path }  " 
97+         validator_id  =  manifest .id 
98+         import_path  =  ValidatorPackageService .get_import_path_from_validator_id (
99+             validator_id 
100+         )
101+ 
102+         import_line  =  f"{ import_path }  " 
99103
100104        # Reload or import the module 
101105        return  ValidatorPackageService .reload_module (import_line )
@@ -112,14 +116,15 @@ def get_org_and_package_dirs(
112116
113117    @staticmethod  
114118    def  add_to_hub_inits (manifest : Manifest , site_packages : str ):
119+         validator_id  =  manifest .id 
115120        org_package  =  ValidatorPackageService .get_org_and_package_dirs (manifest )
116121        exports : List [str ] =  manifest .exports  or  []
117122        sorted_exports  =  sorted (exports , reverse = True )
118-         module_name  =  manifest .module_name 
119-         relative_path  =  "." .join ([* org_package , module_name ])
120-         import_line  =  (
121-             f"from guardrails.hub.{ relative_path }   import { ', ' .join (sorted_exports )}  " 
123+ 
124+         import_path  =  ValidatorPackageService .get_import_path_from_validator_id (
125+             validator_id 
122126        )
127+         import_line  =  f"from { import_path }   import { ', ' .join (sorted_exports )}  " 
123128
124129        hub_init_location  =  os .path .join (
125130            site_packages , "guardrails" , "hub" , "__init__.py" 
@@ -179,14 +184,29 @@ def get_module_path(package_name):
179184        return  package_path 
180185
181186    @staticmethod  
182-     def  get_module_name ( package_uri : str ):
183-         if  not  package_uri .startswith ("hub://" ):
187+     def  get_validator_id ( validator_uri : str ):
188+         if  not  validator_uri .startswith ("hub://" ):
184189            raise  InvalidHubInstallURL (
185190                "Invalid URI! The package URI must start with 'hub://'" 
186191            )
187192
188-         module_name  =  package_uri .replace ("hub://" , "" )
189-         return  module_name 
193+         validator_uri_with_version  =  validator_uri .replace ("hub://" , "" )
194+ 
195+         validator_id_version_regex  =  (
196+             r"(?P<validator_id>[\/a-zA-Z0-9\-_]+)(?P<version>.*)" 
197+         )
198+         match  =  re .match (validator_id_version_regex , validator_uri_with_version )
199+         validator_version  =  None 
200+ 
201+         if  match :
202+             validator_id  =  match .group ("validator_id" )
203+             validator_version  =  (
204+                 match .group ("version" ).strip () if  match .group ("version" ) else  None 
205+             )
206+         else :
207+             validator_id  =  validator_uri_with_version 
208+ 
209+         return  (validator_id , validator_version )
190210
191211    @staticmethod  
192212    def  get_install_url (manifest : Manifest ) ->  str :
@@ -207,18 +227,19 @@ def get_install_url(manifest: Manifest) -> str:
207227    def  run_post_install (
208228        manifest : Manifest , site_packages : str , logger = guardrails_logger 
209229    ):
210-         org_package  =  ValidatorPackageService . get_org_and_package_dirs ( manifest ) 
230+         validator_id  =  manifest . id 
211231        post_install_script  =  manifest .post_install 
232+ 
212233        if  not  post_install_script :
213234            return 
214235
215-         module_name  =  manifest .module_name 
236+         import_path  =  ValidatorPackageService .get_import_path_from_validator_id (
237+             validator_id 
238+         )
239+ 
216240        relative_path  =  os .path .join (
217241            site_packages ,
218-             "guardrails" ,
219-             "hub" ,
220-             * org_package ,
221-             module_name ,
242+             import_path ,
222243            post_install_script ,
223244        )
224245
@@ -255,67 +276,52 @@ def get_hub_directory(manifest: Manifest, site_packages: str) -> str:
255276        org_package  =  ValidatorPackageService .get_org_and_package_dirs (manifest )
256277        return  os .path .join (site_packages , "guardrails" , "hub" , * org_package )
257278
279+     @staticmethod  
280+     def  get_normalized_package_name (validator_id : str ):
281+         validator_id_parts  =  validator_id .split ("/" )
282+         concatanated_package_name  =  (
283+             f"{ validator_id_parts [0 ]}  -grhub-{ validator_id_parts [1 ]}  " 
284+         )
285+         pep_503_package_name  =  canonicalize_name (concatanated_package_name )
286+         return  pep_503_package_name 
287+ 
288+     @staticmethod  
289+     def  get_import_path_from_validator_id (validator_id ):
290+         pep_503_package_name  =  ValidatorPackageService .get_normalized_package_name (
291+             validator_id 
292+         )
293+         return  pep_503_package_name .replace ("-" , "_" )
294+ 
258295    @staticmethod  
259296    def  install_hub_module (
260-         module_manifest :  Manifest ,
261-         site_packages :  str ,
297+         validator_id :  str ,
298+         validator_version :  Optional [ str ]  =   "" ,
262299        quiet : bool  =  False ,
263300        upgrade : bool  =  False ,
264301        logger = guardrails_logger ,
265302    ):
266-         install_url  =  ValidatorPackageService .get_install_url (module_manifest )
267-         install_directory  =  ValidatorPackageService .get_hub_directory (
268-             module_manifest , site_packages 
303+         pep_503_package_name  =  ValidatorPackageService .get_normalized_package_name (
304+             validator_id 
269305        )
306+         validator_version  =  validator_version  if  validator_version  else  "" 
307+         full_package_name  =  f"{ pep_503_package_name } { validator_version }  " 
308+ 
309+         guardrails_token  =  settings .rc .token 
270310
271-         pip_flags  =  [f"--target={ install_directory }  " , "--no-deps" ]
311+         pip_flags  =  [
312+             f"--index-url=https://__token__:{ guardrails_token }  @e4c4zula06.execute-api.us-east-1.amazonaws.com/simple" ,
313+             "--extra-index-url=https://pypi.org/simple" ,
314+         ]
272315
273316        if  upgrade :
274317            pip_flags .append ("--upgrade" )
275318
276319        if  quiet :
277320            pip_flags .append ("-q" )
278321
279-         # Install validator module in namespaced directory under guardrails.hub 
280-         download_output  =  pip_process ("install" , install_url , pip_flags , quiet = quiet )
322+         # Install from guardrails hub pypi server with public pypi index as fallback 
323+         download_output  =  pip_process (
324+             "install" , full_package_name , pip_flags , quiet = quiet 
325+         )
281326        if  not  quiet :
282327            logger .info (download_output )
283- 
284-         # Install validator module's dependencies in normal site-packages directory 
285-         inspect_output  =  pip_process (
286-             "inspect" ,
287-             flags = [f"--path={ install_directory }  " ],
288-             format = json_format ,
289-             quiet = quiet ,
290-             no_color = True ,
291-         )
292- 
293-         # throw if inspect_output is a string. Mostly for pyright 
294-         if  isinstance (inspect_output , str ):
295-             logger .error ("Failed to inspect the installed package!" )
296-             raise  FailedPackageInspection 
297- 
298-         dependencies  =  (
299-             Stack (* inspect_output .get ("installed" , []))
300-             .at (0 , {})
301-             .get ("metadata" , {})  # type: ignore 
302-             .get ("requires_dist" , [])  # type: ignore 
303-         )
304-         requirements  =  list (filter (lambda  dep : "extra"  not  in   dep , dependencies ))
305-         for  req  in  requirements :
306-             if  "git+"  in  req :
307-                 install_spec  =  req .replace (" " , "" )
308-                 dep_install_output  =  pip_process ("install" , install_spec , quiet = quiet )
309-                 if  not  quiet :
310-                     logger .info (dep_install_output )
311-             else :
312-                 req_info  =  Stack (* req .split (" " ))
313-                 name  =  req_info .at (0 , "" ).strip ()  # type: ignore 
314-                 versions  =  req_info .at (1 , "" ).strip ("()" )  # type: ignore 
315-                 if  name :
316-                     install_spec  =  name  if  not  versions  else  f"{ name } { versions }  " 
317-                     dep_install_output  =  pip_process (
318-                         "install" , install_spec , quiet = quiet 
319-                     )
320-                     if  not  quiet :
321-                         logger .info (dep_install_output )
0 commit comments