Repository: kubeflow/pipelines

Issue:
[feature] Print deep links outside of Jupyter notebooks
### Feature Area

/area sdk

### What feature would you like to see?
Currently, `client.py` has 5 blocks that look like this:
```python
        if self._is_ipython():
            import IPython
            html = \
                f'<a href="{self._get_url_prefix()}/#/experiments/details/{experiment.id}" target="_blank" >Experiment details</a>.'
            IPython.display.display(IPython.display.HTML(html))
```

This is a really helpful feature but it's only available when interacting with the SDK from IPython. It would be nice (and trivial to implement) if links were printed outside of IPython. 

I'll submit a PR momentarily that implements this. 

---

<!-- Don't delete message below to encourage users to support your feature request! -->
Love this idea? Give it a 👍. 


Discussion:

Resolving patch:
diff --git a/sdk/python/kfp/client/client.py b/sdk/python/kfp/client/client.py
--- a/sdk/python/kfp/client/client.py
+++ b/sdk/python/kfp/client/client.py
@@ -477,11 +477,14 @@ def create_experiment(
                 resource_references=resource_references)
             experiment = self._experiment_api.create_experiment(body=experiment)
 
+        link = f"{self._get_url_prefix()}/#/experiments/details/{experiment.id}"
         if self._is_ipython():
             import IPython
-            html = \
-                f'<a href="{self._get_url_prefix()}/#/experiments/details/{experiment.id}" target="_blank" >Experiment details</a>.'
+            html = f'<a href="{link}" target="_blank" >Experiment details</a>.'
             IPython.display.display(IPython.display.HTML(html))
+        else:
+            print(f"Experiment details: {link}")
+
         return experiment
 
     def get_pipeline_id(self, name: str) -> Optional[str]:
@@ -781,12 +784,14 @@ def run_pipeline(
 
         response = self._run_api.create_run(body=run_body)
 
+        link = f"{self._get_url_prefix()}/#/runs/details/{response.run.id}"
         if self._is_ipython():
             import IPython
-            html = (
-                f'<a href="{self._get_url_prefix()}/#/runs/details/{response.run.id}" target="_blank" >Run details</a>.'
-            )
+            html = (f'<a href="{link}" target="_blank" >Run details</a>.')
             IPython.display.display(IPython.display.HTML(html))
+        else:
+            print(f"Run details: {link}")
+
         return response.run
 
     def archive_run(self, run_id: str) -> dict:
@@ -1372,10 +1377,14 @@ def upload_pipeline(
         validate_pipeline_resource_name(pipeline_name)
         response = self._upload_api.upload_pipeline(
             pipeline_package_path, name=pipeline_name, description=description)
+        link = f"{self._get_url_prefix()}/#/pipelines/details/{response.id}"
         if self._is_ipython():
             import IPython
-            html = f'<a href={self._get_url_prefix()}/#/pipelines/details/{response.id}>Pipeline details</a>.'
+            html = f'<a href="{link}" target="_blank" >Pipeline details</a>.'
             IPython.display.display(IPython.display.HTML(html))
+        else:
+            print(f"Pipeline details: {link}")
+
         return response
 
     def upload_pipeline_version(
@@ -1417,10 +1426,14 @@ def upload_pipeline_version(
         response = self._upload_api.upload_pipeline_version(
             pipeline_package_path, **kwargs)
 
+        link = f"{self._get_url_prefix()}/#/pipelines/details/{response.id}"
         if self._is_ipython():
             import IPython
-            html = f'<a href={self._get_url_prefix()}/#/pipelines/details/{response.id}>Pipeline details</a>.'
+            html = f'<a href="{link}" target="_blank" >Pipeline details</a>.'
             IPython.display.display(IPython.display.HTML(html))
+        else:
+            print(f"Pipeline details: {link}")
+
         return response
 
     def get_pipeline(self, pipeline_id: str) -> kfp_server_api.ApiPipeline:


Test patch: