Repository: pytorch
Issue: Public API definition is not compatible with `torch.testing`
Labels: triaged,module: testing,module: python frontend

`torch.testing` uses the following layout

```
torch/testing
├── _comparison.py
├── _creation.py
├── __init__.py
├── _internal
│   ├── ...
```

`__init__.py` has the following contents:

https://github.com/pytorch/pytorch/blob/be5108d5f925e9b4ed2a5380dc41e33345a6ec6b/torch/testing/__init__.py#L1-L3

Meaning, we have two private namespaces, namely `_comparison` and `_creation`, in which everything is implemented and only very few functions that should be public are imported into the `torch.testing` namespace. 

This standard Python packaging, but it violates the [Public API definition and documentation](https://github.com/pytorch/pytorch/wiki/Public-API-definition-and-documentation)

For example

```pycon
>>> fn = torch.testing.assert_close
>>> fn.__module__
'torch.testing._comparison'
>>> fn.__name__
'assert_close'
```

> Other objects, in particular functions and classes, are public if 

- > they have a `__module__` attribute, 

  :heavy_check_mark:
- > it starts with `torch.`,

  :heavy_check_mark:
- > corresponds to a public submodule and

  :heavy_multiplication_x: 
- > the object's name itself does not start with “_”.

  :heavy_check_mark:

`assert_close` is _exposed_ in an public submodule, but it is _implemented_ in a private one.

Unfortunately, without extra hacks, it is impossible to infer the exposure point by just looking at the object. Up until now, all functions that `__init__.py` exposes are part of the allow list

https://github.com/pytorch/pytorch/blob/c6942dbbfbf836450898aa9a0c08aefe437d0765/test/allowlist_for_publicAPI.json#L1026

and thus will be ignored by

https://github.com/pytorch/pytorch/blob/c6942dbbfbf836450898aa9a0c08aefe437d0765/test/test_public_bindings.py#L280

However, #90005 tries to add another function to `torch.testing` with the same setup as before. This now trips CI:

```
# torch.testing.assert_not_close:
  - Is public: it is an attribute that does not start with `_` on a module that does not have `__all__` defined
  - Does NOT look public: because its `__module__` attribute (`torch.testing._comparison`) is not within the torch library or does not start with the submodule where it is defined (`torch.testing`)
  - You can do either of these two things to fix this problem:
    - To make it NOT public: either define a `__all__` for `torch.testing` or add a `_` at the beginning of the name
    - To make it look public: make sure the `__module__` is properly set and points to a submodule of `torch.testing`
```

Adding a redundant `__all__` to `torch/testing/__init__.py` also doesn't help:

```
# torch.testing.assert_not_close:
  - Is public: it is inside the module's (`torch.testing`) `__all__`
  - Does NOT look public: because its `__module__` attribute (`torch.testing._comparison`) is not within the torch library or does not start with the submodule where it is defined (`torch.testing`)
  - You can do either of these two things to fix this problem:
    - To make it NOT public: remove it from the modules's (`torch.testing`) `__all__`
    - To make it look public: make sure the `__module__` is properly set and points to a submodule of `torch.testing`
```

Since the first suggestion is not helpful here, we can follow the second one, but it is hacky. For example, we can place the following snippet at the end of`__init__.py`:

```py
def fix_module_attr():
    for name, obj in globals().items():
        if not name.startswith("_"):
            obj.__module__ = __package__


fix_module_attr()
del fix_module_attr
```

Another, and my preferred, solution would be to reword the policy and in turn fix the test to allow simple scenarios as above. I'm also ok to just add all `torch.testing` functions to the allow list, since there are only a few and I don't expect a sudden influx in the future. However

https://github.com/pytorch/pytorch/blob/c6942dbbfbf836450898aa9a0c08aefe437d0765/test/test_public_bindings.py#L292-L295


cc @albanD