Repository: pydantic/pydantic

Issue:
Incorrect signature for instances of models
# Bug

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
             pydantic version: 1.5
            pydantic compiled: False
                 install path: /home/jrootjunior/.cache/pypoetry/virtualenvs/aiogram-BAWpo_Vh-py3.8/lib/python3.8/site-packages/pydantic
               python version: 3.8.2 (default, Apr  8 2020, 14:31:25)  [GCC 9.3.0]
                     platform: Linux-5.6.3-arch1-1-x86_64-with-glibc2.2.5
     optional deps. installed: ['typing-extensions']
```

## Explanation
Feature from #1034 generates incorrect signature for callable instances of models.

## Code examples
### Current behavior
```py
import inspect

from pydantic import BaseModel


class MyModel(BaseModel):
    foo: str

    def __call__(self, bar: str):
        pass


print(inspect.getfullargspec(MyModel))
print(inspect.getfullargspec(MyModel(foo="baz")))
```
will print
```py
FullArgSpec(args=[], varargs=None, varkw=None, defaults=None, kwonlyargs=['foo'], kwonlydefaults=None, annotations={'return': None, 'foo': <class 'str'>})
FullArgSpec(args=[], varargs=None, varkw=None, defaults=None, kwonlyargs=['foo'], kwonlydefaults=None, annotations={'return': None, 'foo': <class 'str'>})
```
Spec for class is correct in most of cases. Is only needed to add `self` to `args`
Spec for instance of that model is incorrect in due to instance can not be called with this spec

### Expected behavior
```py
import inspect


class MyModel:
    def __init__(self, *, foo: str):
        self.foo = foo

    def __call__(self, bar: str):
        pass


print(inspect.getfullargspec(MyModel))
print(inspect.getfullargspec(MyModel(foo="baz")))
```
will print
```py
FullArgSpec(args=['self'], varargs=None, varkw=None, defaults=None, kwonlyargs=['foo'], kwonlydefaults=None, annotations={'foo': <class 'str'>})
FullArgSpec(args=['self', 'bar'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={'bar': <class 'str'>}
```

`getfullargspec` for class returns spec of `__init__`
`getfullargspec` for instance returns spec of `__call__` method


### Another expected behavior
```py
import inspect


class MyModel:
    def __init__(self, *, foo: str):
        self.foo = foo


print(inspect.getfullargspec(MyModel))
print(inspect.getfullargspec(MyModel(foo="baz")))
```
will print
```py
FullArgSpec(args=['self'], varargs=None, varkw=None, defaults=None, kwonlyargs=['foo'], kwonlydefaults=None, annotations={'foo': <class 'str'>})
Traceback (most recent call last):
  File "/usr/lib/python3.8/inspect.py", line 1123, in getfullargspec
    sig = _signature_from_callable(func,
  File "/usr/lib/python3.8/inspect.py", line 2216, in _signature_from_callable
    raise TypeError('{!r} is not a callable object'.format(obj))
TypeError: <__main__.MyModel object at 0x7fb1f405b490> is not a callable object

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/jrootjunior/work/aiogram3/expetiment.py", line 19, in <module>
    print(inspect.getfullargspec(MyModel(foo="baz")))
  File "/usr/lib/python3.8/inspect.py", line 1132, in getfullargspec
    raise TypeError('unsupported callable') from ex
TypeError: unsupported callable
```

`getfullargspec` for class returns spec of `__init__`
`getfullargspec` for instance raises an error about instance is not callable.





Comments:
thanks for reporting, PR welcome.
Just saw this, ugh. Will make a PR soon :)

Patch:
diff --git a/pydantic/main.py b/pydantic/main.py
--- a/pydantic/main.py
+++ b/pydantic/main.py
@@ -35,6 +35,7 @@
 from .types import PyObject, StrBytes
 from .typing import AnyCallable, AnyType, ForwardRef, is_classvar, resolve_annotations, update_field_forward_refs
 from .utils import (
+    ClassAttribute,
     GetterDict,
     Representation,
     ValueItems,
@@ -300,7 +301,8 @@ def __new__(mcs, name, bases, namespace, **kwargs):  # noqa C901
         }
 
         cls = super().__new__(mcs, name, bases, new_namespace, **kwargs)
-        cls.__signature__ = generate_model_signature(cls.__init__, fields, config)
+        # set __signature__ attr only for model class, but not for its instances
+        cls.__signature__ = ClassAttribute('__signature__', generate_model_signature(cls.__init__, fields, config))
         return cls
 
 
diff --git a/pydantic/utils.py b/pydantic/utils.py
--- a/pydantic/utils.py
+++ b/pydantic/utils.py
@@ -45,6 +45,7 @@
     'GetterDict',
     'ValueItems',
     'version_info',  # required here to match behaviour in v1.3
+    'ClassAttribute',
 )
 
 
@@ -439,3 +440,23 @@ def _normalize_indexes(
 
     def __repr_args__(self) -> 'ReprArgs':
         return [(None, self._items)]
+
+
+class ClassAttribute:
+    """
+    Hide class attribute from its instances
+    """
+
+    __slots__ = (
+        'name',
+        'value',
+    )
+
+    def __init__(self, name: str, value: Any) -> None:
+        self.name = name
+        self.value = value
+
+    def __get__(self, instance: Any, owner: Type[Any]) -> None:
+        if instance is None:
+            return self.value
+        raise AttributeError(f'{self.name!r} attribute of {owner.__name__!r} is class-only')


Test patch:
diff --git a/tests/test_model_signature.py b/tests/test_model_signature.py
--- a/tests/test_model_signature.py
+++ b/tests/test_model_signature.py
@@ -126,3 +126,15 @@ class Config:
             extra = Extra.allow
 
     assert _equals(str(signature(Model)), '(extra_data: int = 1, **foobar: Any) -> None')
+
+
+def test_signature_is_class_only():
+    class Model(BaseModel):
+        foo: int = 123
+
+        def __call__(self, a: int) -> bool:
+            pass
+
+    assert _equals(str(signature(Model)), '(*, foo: int = 123) -> None')
+    assert _equals(str(signature(Model())), '(a: int) -> bool')
+    assert not hasattr(Model(), '__signature__')
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -12,7 +12,15 @@
 from pydantic.dataclasses import dataclass
 from pydantic.fields import Undefined
 from pydantic.typing import display_as_type, is_new_type, new_type_supertype
-from pydantic.utils import ValueItems, deep_update, get_model, import_string, lenient_issubclass, truncate
+from pydantic.utils import (
+    ClassAttribute,
+    ValueItems,
+    deep_update,
+    get_model,
+    import_string,
+    lenient_issubclass,
+    truncate,
+)
 from pydantic.version import version_info
 
 try:
@@ -298,3 +306,17 @@ def test_version_info():
 
 def test_version_strict():
     assert str(StrictVersion(VERSION)) == VERSION
+
+
+def test_class_attribute():
+    class Foo:
+        attr = ClassAttribute('attr', 'foo')
+
+    assert Foo.attr == 'foo'
+
+    with pytest.raises(AttributeError, match="'attr' attribute of 'Foo' is class-only"):
+        Foo().attr
+
+    f = Foo()
+    f.attr = 'not foo'
+    assert f.attr == 'not foo'