Repository: pytorch
Issue: Support passing Python code as a string to torch.jit.script
Labels: oncall: jit

### 🚀 The feature, motivation and pitch

As far as I can tell, there is currently **no way** to dynamically create functions at runtime and compile them to TorchScript using the public Python API. If you try to create a function with `exec()`, then pass it to `torch.jit.script`, you get an error like this:
```
OSError: Can't get source for <function add at 0x7fed80653d90>. TorchScript requires source access
in order to carry out compilation, make sure original .py files are available.
```
This is because we're using `inspect.getsourcelines` to get the source code of functions passed to `torch.jit.script`, and it seems to fail for any and all dynamically generated functions.

There are certain cases, however, in which it would be useful to be able to construct Python code at runtime and then compile it to TorchScript. For example, I am currently working on PR #73066, which will add support for dataclasses in TorchScript. To implement that feature, I'm dynamically generating the implementations of `__init__`, `__eq__`, and so on at runtime based on the dataclass fields, and then compiling them. Because of this I know it would be quite easy to add this kind of functionality to the public API; I'm fairly confident it would only require modifying a few outward-facing Python functions in the `jit` module, with no changes to the C++ backend.

There are many potential use cases for this functionality, including custom class decorators and property-based unit testing. In fact this idea occurred to me this evening as I was writing Hypothesis-based unit tests for the dataclasses PR, and realized that I couldn't dynamically generate dataclasses for the unit tests without using private APIs. In my view this would also be a great addition to the graph manipulation features in `torch.fx`, allowing for even more flexible runtime manipulations of code while maintaining the ability to compile to TorchScript.

I'm imagining that the syntax would look more or less like this:
```
add = torch.jit.script("""
	def add(a, b):
		return a + b
""")
add(torch.tensor(1.0), torch.tensor(1.0))
# Output: torch.tensor(2.0)
```
I'm expecting we could also support dynamic creation of TorchScript classes:
```
my_class = torch.jit.script("""
	@dataclass
	class TanhGaussian:
		mu: torch.Tensor
		sigma: torch.Tensor

		def log_prob(self, value):
			...
""")
dist = my_class(torch.zeros(5), torch.ones(5))
dist.log_prob(...)
```
By simply inspecting the AST for the provided code, we could enforce the invariant that only one top-level function definition or class can be compiled at a time.

I would be more than happy to implement this feature myself, either as an addition to my work-in-progress PR #73066 or as a separate one. 

### Alternatives

One alternative would be to create a new function, say `torch.jit.script_string`, with this functionality. I'm not strongly opposed to this idea, but it seems like an unnecessary complication of the API. We already allow passing multiple different types of object to `torch.jit.script` (functions, `nn.Modules`, arbitrary user-defined classes and objects), so why not also allow strings?

### Additional context

_No response_