pydantic BaseModel with instance variable - python

I'm using pydantic with fastapi.
And, I make Model like this.
# model.py
from multiprocessing import RLock
from pydantic import BaseModel
class ModelA(BaseModel):
file_1: str = 'test'
def __init__(self, **data: Any):
super().__init__(**data)
self._lock = RLock()
self._instance_variable: int = 1
#property
def lock(self):
return self._lock
#property
def instance_variable(self) -> int:
with self.lock:
return self._instance_variable
#instance_variable.setter
def instance_variable(self, v: int) -> int:
assert isinstance(v, int)
with self.lock:
self._instance_variable = v
And I make test like this
# test_model_a.py
def test_model_a():
instance = ModelA()
assert instance.json() == '{"field_1": "test"}'
After, I run the test, but the instance can't create with this error.
E ValueError: "ModelA" object has no field "_lock"
So, How can I pass this test...?
Please... help me...

You need to use PrivateAttr field. And instead of a setter, use a workaround with __setattr__
from multiprocessing import RLock, synchronize
from typing import Any
from pydantic import BaseModel, PrivateAttr
class ModelA(BaseModel):
file_1: str = 'test'
_lock: synchronize.RLock = PrivateAttr()
_instance_variable: int = PrivateAttr()
def __init__(self, **data: Any):
super().__init__(**data)
self._lock = RLock()
self._instance_variable: int = 1
#property
def lock(self):
return self._lock
#property
def instance_variable(self) -> int:
with self.lock:
return self._instance_variable
def __setattr__(self, key, val):
if key == "instance_variable":
assert isinstance(val, int)
with self.lock:
self._instance_variable = val
return
super().__setattr__(key, val)
You can also avoid using a custom __init__
class ModelA(BaseModel):
file_1: str = 'test'
_lock: synchronize.RLock = PrivateAttr(default_factory=RLock)
_instance_variable: int = PrivateAttr(1)
# ...

Related

How to, in Python3, make the typing system know a subclass instance created by `super().*` method has subclass methods

from typing import Type, TypeVar
T = TypeVar('T', bound='A')
class A:
#classmethod
def create(cls: Type[T]) -> T:
return cls()
class B(A):
def do(self):
...
#classmethod
def create(cls):
obj = super().create()
obj.do()
# ^^ <- complaint
if __name__ == '__main__':
b = B.create() # OK, no error
In the above example, the type-checker complains about do method invocation in the subclass create method. Is there a way to work around this?
Adding a type hint on B.create seems to do the trick, even with strict options of mypy enabled
from typing import Type, TypeVar
T = TypeVar('T', bound='A')
U = TypeVar('U', bound='B')
class A:
#classmethod
def create(cls: Type[T]) -> T:
return cls()
class B(A):
def do(self) -> None:
pass
#classmethod
def create(cls: Type[U]) -> U:
obj = super().create()
obj.do()
return obj
if __name__ == '__main__':
b = B.create()

How to properly type-hint method that returns abstract class implementation result type

I have a problem with type-hinting the results of my method.
It sends operation to the server, and after the response it returns parsed operation result. Every ServerOperation has its' own result type and I need to fetch this type somehow.
def send(self, operation: ServerOperation): # what should I return here?
command = operation.get_command(self.__request_executor.conventions)
self.__request_executor.execute_command(command)
if isinstance(operation, ServerOperation):
return command.result
Operation class
class ServerOperation(Generic[T]):
#abstractmethod
def get_command(self, conventions: "DocumentConventions") -> RavenCommand[T]:
raise NotImplementedError()
And the simplified RavenCommand class
class RavenCommand(Generic[ResultClass]):
def __init__(self, result_class: Type[ResultClass] = None):
self._result_class = result_class
self.result: Union[None, ResultClass] = None
How should i approach this?
Implementation example
class GetClientConfigurationOperation(MaintenanceOperation):
def get_command(self, conventions: "DocumentConventions") -> "RavenCommand[GetClientConfigurationOperation.Result]":
return self.GetClientConfigurationCommand()
class GetClientConfigurationCommand(RavenCommand):
def __init__(self):
super().__init__(GetClientConfigurationOperation.Result)
def is_read_request(self) -> bool:
return False
def create_request(self, node: ServerNode) -> requests.Request:
return requests.Request(method="GET", url=f"{node.url}/databases/{node.database}/configuration/client")
def set_response(self, response: str, from_cache: bool) -> None:
if response is None:
return
self.result = GetClientConfigurationOperation.Result.from_json(json.loads(response))
class Result:
def __init__(self, etag: int, configuration: ClientConfiguration):
self.etag = etag
self.configuration = configuration
#staticmethod
def from_json(json_dict: dict) -> GetClientConfigurationOperation.Result:
return GetClientConfigurationOperation.Result(
json_dict["Etag"], ClientConfiguration.from_json(json_dict["Configuration"])
)

Mypy marks error when using TypeVar type inside TypeDict derived class

I have the following code:
WriterMeta = typing.TypeVar('WriterMeta', GSheetWritable, S3Writable, LocalWritable)
class WriterDesc(typing.TypedDict):
name: str
meta: WriterMeta
class DataWriter(ABC, typing.Generic[WriterMeta]):
"""Defines the interface for all data writers"""
#abstractproperty
def connector(self) -> typing.Any:
pass
#abstractproperty
def target(self) -> typing.Any:
pass
#abstractmethod
def write(self, data: pd.DataFrame, meta: WriterMeta, versionize: bool):
"""This method performs the writing of 'data'.
Every concrete class implementing this method must implement its writing
using 'connector'
"""
pass
class LocalOutputWriter(DataWriter[LocalWritable]):
def __init__(self, settings, timestamp, env):
self._connector = None
self._target = None
self.timestamp = timestamp
self.settings = settings
self.env = env
#property
def connector(self) -> typing.Any:
return self._connector
#property
def target(self) -> typing.Any:
return self._target
def get_file_name(self, data_name: str, versionize: bool) -> str:
if versionize:
file_name = "{}_{}_{}_{}".format(
self.settings["scm_week"],
data_name,
self.settings["volume_source"],
self.timestamp,
)
else:
file_name = data_name
file_name = file_name + ".csv"
return file_name
def write(self, data: pd.DataFrame, meta: LocalWritable, versionize: bool):
data = data.replace({np.nan: "NaN", np.Inf: "Inf"})
file_name = self.get_file_name(meta["file_name"], versionize)
print("Writing '{}' file to local.".format(file_name))
data.to_csv(file_name)
The code works as expected, but mypy marks the following error:
error: Type variable "outputs.WriterMeta" is unbound
note: (Hint: Use "Generic[WriterMeta]" or "Protocol[WriterMeta]" base class to bind "WriterMeta" inside a class)
How can use a TypeVar datatype inside a TypeDict derived class?

How can I show the code that is generated when using #dataclass class decorator?

Python 3.7 introduces the dataclasses module that contains a #dataclass decorator. This decorator can generate class functions. How can I print these generated functions?
I asked the same question myself. Dataclasses part project should have the verbose option, but is not.
Found out this video, helpful. According to the video dataclasses.py is a code generator. So this should be our first idea how to get the code.
I tried this code:
from dataclasses import dataclass
import inspect
import os
from uncompyle6 import PYTHON_VERSION, deparse_code2str
#dataclass
class C:
name: str
value: int = 34
inspect.getmembers(C) #('__init__', <function __init__(self, name: str, value: int = 34) -> None>)
co= C.__init__.__code__ # let's find out code for the __init__ method from code object
code = deparse_code2str(
code=co,
version=PYTHON_VERSION,
out=open(os.devnull, "w"))
print(code)
Will print
self.name = name
self.value = value
The code acutally uses the inspector to understand the class, and then to decompile the methods using Python decompiler.
Here are the methods discovered:
def __eq__(self, other):
if other.__class__ is self.__class__:
return (self.name, self.value) == (
other.name,
other.value,
)
else:
return NotImplemented
def __init__(self, name: str, value: int = 34) -> None:
self.name = name
self.value = value
def __repr__(self):
key = (id(self), _thread.get_ident())
if key in repr_running:
return "..."
else:
repr_running.add(key)
try:
result = user_function(self)
finally:
repr_running.discard(key)
return result
There is actually a project that is doing #dataclass discovery. I installed it and it worked.
from dataclasses import dataclass
import inspect
import os
import dis
from DataclassInspector.inspector import Inspector
#dataclass
class C:
name: str
value: int = 34
inspected = Inspector(C)
print(inspected._generate_code())
Provided the output like this:
from dataclasses import Field, _MISSING_TYPE, _DataclassParams
class C:
__dataclass_fields__ = {'name': "Field(name='name', type=str, default=_MISSING_TYPE, default_factory=_MISSING_TYPE, init=True, repr=True, hash=None, compare=True, metadata={}, _field_type=_FIELD)", 'value': "Field(name='value', type=int, default=34, default_factory=_MISSING_TYPE, init=True, repr=True, hash=None, compare=True, metadata={}, _field_type=_FIELD)"}
__dataclass_params__ = _DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=False)
name: str
value: int = 34
def __eq__(self, other):
if other.__class__ is self.__class__:
return (self.name, self.value) == (other.name, other.value)
else:
return NotImplemented
__hash__ = None
def __init__(self, name: str, value: int = 34) -> None:
self.name = name
self.value = value
def __repr__(self):
key = (
id(self), _thread.get_ident())
if key in repr_running:
return '...'
else:
repr_running.add(key)
try:
result = user_function(self)
finally:
repr_running.discard(key)
return result
After inspecting the dataclass implementation, the methods seems to be generated by a dataclasses._create_fn. To get the original generated code, I mocked the function as:
import dataclasses
_original_create_fn = dataclasses._create_fn
def _new_create_fn(name, args, body, **kwargs):
args_str = ', '.join(args)
body_str = '\n'.join(' ' + l for l in body)
print(f'def {name}({args_str}):\n{body_str}\n')
return _original_create_fn(name, args, body, **kwargs)
dataclasses._create_fn = _new_create_fn
# After the dataclasses as been mocked, creating new dataclass
# will display their source code
#dataclasses.dataclass
class A:
x: int
y: int
Which display something like:
def __init__(self, x:_type_x, y:_type_y):
self.x=x
self.y=y
def __repr__(self):
return self.__class__.__qualname__ + f"(x={self.x!r}, y={self.y!r})"
def __eq__(self, other):
if other.__class__ is self.__class__:
return (self.x,self.y,)==(other.x,other.y,)
return NotImplemented

How to let python type checker know that it should return a new instance of its class?

I wanna use classmethod to return a new instance of current class, and I have try some code like below, but it raise a NameError('name 'T' is not defined')
Put the code T = TypeVar('T', bound=A) on class A above does not work neither.
Is there any good idea to handle it?
import json
from typing import TypeVar
class A(dict):
def __init__(self, name):
super(dict, self).__init__()
self["name"] = name
#classmethod
def foo(cls: T, args: str)->T:
return json.loads(args)
T = TypeVar('T', bound=A)
class B(A):
pass
b = B(name='B')
# True
print(isinstance(b.foo(json.dumps(b)),B))
Use a string to make a forward reference to A and give cls the correct type of Type[T]:
import json
from typing import Type, TypeVar
T = TypeVar('T', bound='A')
class A(dict):
def __init__(self, name: str) -> None:
super().__init__(name=name)
#classmethod
def foo(cls: Type[T], args: str) -> T:
return cls(**json.loads(args))
class B(A):
def is_b(self) -> bool:
return True
b = B.foo('{"name": "foo"}')
print(b.is_b())

Categories

Resources