Repository: ivy-llc/ivy
Pull request: fix: Fixing pytest discovery issues
<!--
This template will help you to have a meaningful PR, please follow it and do not leave it blank.
-->

# PR Description

Fixing pytest discovery issues

Specific edge cases addressed:
- `ivy.functional.frontends.jax.numpy.indexing.CClass` is a class object invoked as a function call, parsed in ast extraction.
- `jnp_frontend` namespace causing string manipulation error in retrieving dtypes/device for nested functions.
- dict containing ivy.functional.frontends supported versions does not contain a supported `mindspore` version.

<!--
If there is no related issue, please add a short description about your PR.
-->

## Related Issue

<!--
Please use this format to link other issues with their numbers: Close #123
https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword
-->

Closes #28138

## Checklist

- [ ] Did you add a function?
- [ ] Did you add the tests?
- [ ] Did you run your tests and are your tests passing?
- [x] Did pre-commit not fail on any check?
- [x] Did you follow the steps we provided?

<!--
Please mark your PR as a draft if you realise after the fact that your tests are not passing or
that your pre-commit check has some failures.

Here are some relevant resources regarding tests and pre-commit:

https://unify.ai/docs/ivy/overview/deep_dive/ivy_tests.html
https://unify.ai/docs/ivy/overview/deep_dive/formatting.html#pre-commit

-->

### Socials

<!--
If you have Twitter, please provide it here otherwise just ignore this.
-->


Problem statement:
Fixing pytest discovery issues on vscode
Pytest discovery on vscode fails for our tests as of now with multiple errors regarding imports, unsupported dtypes, missing keys, etc. The goal of this task is to find workarounds and make fixes so that pytest discovery on vscode works for our repository.

Hints from issue discussion:
@vedpatwardhan  can you give an example that we can run and produce the error and take look at this . It would be great help thanks
Hey @Vismay-dev, have you already made a start on this issue? If yes, I'll assign this to you 😄
Hey @vedpatwardhan, yes I've already started trying to cross check file import paths for fixing pytest discovery issues.

Commit message: Fixing pytest discovery issues

Patch to review:
diff --git a/ivy/functional/frontends/__init__.py b/ivy/functional/frontends/__init__.py
index 013a7e3fc26f8..4ae0d2ddfa489 100644
--- a/ivy/functional/frontends/__init__.py
+++ b/ivy/functional/frontends/__init__.py
@@ -11,6 +11,7 @@
     "sklearn": "1.3.0",
     "xgboost": "1.7.6",
     "torchvision": "0.15.2.",
+    "mindspore": "2.0.0",
 }
 
 
diff --git a/ivy/functional/frontends/jax/numpy/creation.py b/ivy/functional/frontends/jax/numpy/creation.py
index 0ee3781d3c64b..65de35d7444d1 100644
--- a/ivy/functional/frontends/jax/numpy/creation.py
+++ b/ivy/functional/frontends/jax/numpy/creation.py
@@ -1,7 +1,7 @@
 import ivy
 from ivy.func_wrapper import with_unsupported_dtypes
 from ivy.functional.frontends.jax.array import Array
-import ivy.functional.frontends.jax.numpy as jnp_frontend
+import ivy.functional.frontends.jax.numpy as jnp
 from ivy.functional.frontends.jax.func_wrapper import (
     to_ivy_arrays_and_back,
     outputs_to_frontend_arrays,
@@ -270,7 +270,7 @@ def setdiff1d(ar1, ar2, assume_unique=False, *, size=None, fill_value=None):
             if ivy.is_bool_dtype(ar1.dtype)
             else ivy.to_scalar(ivy.min(ar1))
         )
-        ar1 = jnp_frontend.unique(ar1, size=size and ar1.size, fill_value=val).ivy_array
+        ar1 = jnp.unique(ar1, size=size and ar1.size, fill_value=val).ivy_array
     mask = in1d(ar1, ar2, invert=True).ivy_array
     if size is None:
         return ar1[mask]
@@ -281,7 +281,7 @@ def setdiff1d(ar1, ar2, assume_unique=False, *, size=None, fill_value=None):
             mask = ivy.where(ivy.arange(ar1.size) < n_unique, mask, False)
         return ivy.where(
             ivy.arange(size) < mask.sum(dtype=ivy.int64),
-            ar1[jnp_frontend.where(mask, size=size)[0].ivy_array],
+            ar1[jnp.where(mask, size=size)[0].ivy_array],
             fill_value,
         )
 
diff --git a/ivy/functional/frontends/tensorflow/keras/activations.py b/ivy/functional/frontends/tensorflow/keras/activations.py
index 9abc0a84825ad..603ddf3121387 100644
--- a/ivy/functional/frontends/tensorflow/keras/activations.py
+++ b/ivy/functional/frontends/tensorflow/keras/activations.py
@@ -80,6 +80,14 @@ def get(identifier):
         raise ValueError(f"Could not interpret function identifier: {identifier}")
 
 
+# helper
+# note: defined to avoid AST call extraction of
+# 'tf_frontend.keras.activations.__dict__.items()
+# or 'tf_frontend.keras.activations.__dict__.values()'
+def get_tf_keras_activations():
+    return tf_frontend.keras.activations.__dict__.items()
+
+
 @to_ivy_arrays_and_back
 def hard_sigmoid(x):
     dtype_in = x.dtype
@@ -132,13 +140,15 @@ def serialize(activation, use_legacy_format=False, custom_objects=None):
                 if custom_func == activation:
                     return name
 
+        tf_keras_frontend_activations = get_tf_keras_activations()
+
         # Check if the function is in the ACTIVATION_FUNCTIONS list
         if activation.__name__ in ACTIVATION_FUNCTIONS:
             return activation.__name__
 
         # Check if the function is in the TensorFlow frontend activations
-        elif activation in tf_frontend.keras.activations.__dict__.values():
-            for name, tf_func in tf_frontend.keras.activations.__dict__.items():
+        elif activation in [fn for name, fn in tf_keras_frontend_activations]:
+            for name, tf_func in get_tf_keras_activations():
                 if tf_func == activation:
                     return name
 
diff --git a/ivy/functional/ivy/data_type.py b/ivy/functional/ivy/data_type.py
index 5739c59c551e8..c166c5db8af5a 100644
--- a/ivy/functional/ivy/data_type.py
+++ b/ivy/functional/ivy/data_type.py
@@ -153,7 +153,7 @@ def _nested_get(f, base_set, merge_fn, get_fn, wrapper=set):
             continue
         is_frontend_fn = "frontend" in fn.__module__
         is_backend_fn = "backend" in fn.__module__ and not is_frontend_fn
-        is_einops_fn = "einops" in fn.__name__
+        is_einops_fn = hasattr(fn, "__name__") and "einops" in fn.__name__
         if is_backend_fn:
             f_supported = get_fn(fn, False)
             if hasattr(fn, "partial_mixed_handler"):
@@ -197,6 +197,10 @@ def _nested_get(f, base_set, merge_fn, get_fn, wrapper=set):
                     if "(" in key:
                         key = key.split("(")[0]
                     frontend_module = ".".join(key.split(".")[:-1])
+                    if (
+                        frontend_module == ""
+                    ):  # single edge case: fn='frontend_outputs_to_ivy_arrays'
+                        continue
                     frontend_fl = {key: frontend_fn}
                     res += list(
                         _get_functions_from_string(
diff --git a/ivy/functional/ivy/device.py b/ivy/functional/ivy/device.py
index 9187040ad1ffc..e32b05358ad39 100644
--- a/ivy/functional/ivy/device.py
+++ b/ivy/functional/ivy/device.py
@@ -1169,7 +1169,7 @@ def _get_devices(fn: Callable, complement: bool = True) -> Tuple:
 
     is_backend_fn = "backend" in fn.__module__
     is_frontend_fn = "frontend" in fn.__module__
-    is_einops_fn = "einops" in fn.__name__
+    is_einops_fn = hasattr(fn, "__name__") and "einops" in fn.__name__
     if not is_backend_fn and not is_frontend_fn and not is_einops_fn:
         if complement:
             supported = set(all_devices).difference(supported)
diff --git a/ivy/functional/ivy/general.py b/ivy/functional/ivy/general.py
index f19a1c4b0face..ad56c1c224d5f 100644
--- a/ivy/functional/ivy/general.py
+++ b/ivy/functional/ivy/general.py
@@ -3989,7 +3989,7 @@ def _get_devices_and_dtypes(fn, recurse=True, complement=True):
 
     is_frontend_fn = "frontend" in fn.__module__
     is_backend_fn = "backend" in fn.__module__ and not is_frontend_fn
-    is_einops_fn = "einops" in fn.__name__
+    is_einops_fn = hasattr(fn, "__name__") and "einops" in fn.__name__
     if not is_backend_fn and not is_frontend_fn and not is_einops_fn:
         if complement:
             all_comb = _all_dnd_combinations()
diff --git a/ivy_tests/test_ivy/test_frontends/test_jax/test_numpy/test_indexing.py b/ivy_tests/test_ivy/test_frontends/test_jax/test_numpy/test_indexing.py
index 4d76bfcc9a139..0432ff7d62fc9 100644
--- a/ivy_tests/test_ivy/test_frontends/test_jax/test_numpy/test_indexing.py
+++ b/ivy_tests/test_ivy/test_frontends/test_jax/test_numpy/test_indexing.py
@@ -12,7 +12,7 @@
     _helper_r_,
     _helper_c_,
 )
-import ivy.functional.frontends.jax.numpy as jnp_frontend
+import ivy.functional.frontends.jax.numpy as jnp
 
 
 # --- Helpers --- #
@@ -291,7 +291,7 @@ def test_jax_mask_indices(
 def test_jax_numpy_c_(inputs, backend_fw):
     ret_gt = c_.__getitem__(tuple(inputs))
     with BackendHandler.update_backend(backend_fw):
-        ret = jnp_frontend.c_.__getitem__(tuple(inputs))
+        ret = jnp.c_.__getitem__(tuple(inputs))
     assert np.allclose(ret.ivy_array, ret_gt)
 
 
@@ -331,7 +331,7 @@ def test_jax_numpy_r_(inputs, backend_fw):
     inputs, *_ = inputs
     ret_gt = r_.__getitem__(tuple(inputs))
     with BackendHandler.update_backend(backend_fw):
-        ret = jnp_frontend.r_.__getitem__(tuple(inputs))
+        ret = jnp.r_.__getitem__(tuple(inputs))
     assert np.allclose(ret.ivy_array, ret_gt)
 
 


Code review comments:
diff_hunk: @@ -80,6 +80,14 @@ def get(identifier):
         raise ValueError(f"Could not interpret function identifier: {identifier}")
 
 
+# helper
+# note: defined to avoid AST call extraction of
+# 'tf_frontend.keras.activations.__dict__.items()
+# or 'tf_frontend.keras.activations.__dict__.values()'
+def get_tf_keras_activations():
path: ivy/functional/frontends/tensorflow/keras/activations.py
text: @user1:
let's make this function private if it's a helper

diff_hunk: @@ -12,7 +12,7 @@
     _helper_r_,
     _helper_c_,
 )
-import ivy.functional.frontends.jax.numpy as jnp_frontend
+import ivy.functional.frontends.jax.numpy as jnp
path: ivy_tests/test_ivy/test_frontends/test_jax/test_numpy/test_indexing.py
text: @user1:
I think it would be better if it's imported as `jnp_frontend`, was this leading to an error during pytest discovery?

@author:
I'll change this to jnp_frontend for the test file.

It being imported as 'jnp_frontend' in the actual frontend functions file was causing an error.