Repository: pydantic/pydantic

Issue:
Add Import type
<!-- delete as applicable: -->
# Feature Request

Would there be interest in adding an `Import` type so you could do things like:

```
class AppConfig(BaseModel):
    middleware: List[Import] = []

config = AppConfig(middlewear=['diary.api:make_session'])

...

config.make_session()
```

I have a prototype [here](https://github.com/cjw296/diary/blob/dcfe5da44bef834a9780696393fb8ca000baeb1a/backend/diary/config.py#L11-L29) and I'm happy to work it up into a PR, but thought I'd check if there was interest first.


Discussion:
@cjw296 Sorry I forgot to mention `PyObject` in Exotic Types: https://pydantic-docs.helpmanual.io/#exotic-types

It might help with that.
`PyObject` is for exactly this.
@samuelcolvin - thanks, would you be up for re-opening this issue to better document `PyObject`? I mean, I looked in the exotics section and managed to totally miss it.


yes, totally. Might deserve it's own section.
Related issue: https://github.com/pydantic/pydantic/issues/4803 (Does Github allow linking of issues?)

Also, can you assign to me? I'd like to take a stab at documenting this. @samuelcolvin 
Thanks so much, done.
Just so you're aware, `PyObject` was renamed to `ImportString` on main in preparation for V2. From memory, I think it's functionality changed a bit too.
Appropriately so.

Resolving patch:
diff --git a/docs/examples/types_import_string_serialization.py b/docs/examples/types_import_string_serialization.py
new file mode 100644
--- /dev/null
+++ b/docs/examples/types_import_string_serialization.py
@@ -0,0 +1,42 @@
+from pydantic import BaseModel, ImportString
+from types import BuiltinFunctionType
+
+
+# The following class will not successfully serialize to JSON
+# Since "obj" is evaluated to an object, not a pydantic `ImportString`
+class WithCustomEncodersBad(BaseModel):
+    obj: ImportString
+
+    class Config:
+        json_encoders = {
+            ImportString: lambda x: str(x)
+        }
+
+
+# Create an instance
+m = WithCustomEncodersBad(obj='math.cos')
+
+try:
+    m.json()
+except TypeError as e:
+    print(e)
+
+# Let's do some sanity checks to verify that m.obj is not an "ImportString"
+print(isinstance(m.obj, ImportString))
+print(isinstance(m.obj, BuiltinFunctionType))
+
+
+# So now that we know that after an ImportString is evaluated by Pydantic
+# it results in its underlying object, we can configure our json encoder
+# to account for those specific types
+class WithCustomEncodersGood(BaseModel):
+    obj: ImportString
+
+    class Config:
+        json_encoders = {
+            BuiltinFunctionType: lambda x: str(x)
+        }
+
+
+m = WithCustomEncodersGood(obj='math.cos')
+print(m.json())
diff --git a/docs/examples/types_import_string_usage.py b/docs/examples/types_import_string_usage.py
new file mode 100644
--- /dev/null
+++ b/docs/examples/types_import_string_usage.py
@@ -0,0 +1,40 @@
+from pydantic import BaseModel, ImportString, ValidationError
+
+
+class ImportThings(BaseModel):
+    obj: ImportString
+
+
+# A string value will cause an automatic import
+my_cos = ImportThings(obj='math.cos')
+
+# You can use the imported function as you would expect
+cos_of_0 = my_cos.obj(0)
+assert cos_of_0 == 1
+
+
+# A string whose value cannot be imported will raise an error
+try:
+    ImportThings(obj='foo.bar')
+except ValidationError as e:
+    print(e)
+
+
+# An object defined in the current namespace can indeed be imported,
+# though you should probably avoid doing this (since the ordering of declaration
+# can have an impact on behavior).
+class Foo:
+    bar = 1
+
+
+# This now works
+my_foo = ImportThings(obj=Foo)
+# So does this
+my_foo_2 = ImportThings(obj='__main__.Foo')
+
+
+# Actual python objects can be assigned as well
+from math import cos  # noqa: E402
+my_cos = ImportThings(obj=cos)
+my_cos_2 = ImportThings(obj='math.cos')
+assert my_cos == my_cos_2


Test patch: