graphql-python / graphql-python/graphene

Graphene ObjectType constructor does not make deep copy but instead shallow copy

Đang mở
#1,256 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
🐛 bug
Ngôn ngữ chính
Python
Star
8.2k
Fork
818
Chỉ số merge pull request
Không có pull request nào được merge trong 30 ngày

Mô tả

**Note: for support questions, please use stackoverflow**. This repository's issues are reserved for feature requests and bug reports.

* **What is the current behavior?**
We have types:
```
ChildType(graphene.ObjectType):
enabled = graphene.Boolean(required=True, default_value=False)
def enable(self):
self.enabled = True
def disable(self):
self.enabled = False

ParentType(graphene.ObjectType):
p1prop1 = graphene.Field(ChildType, required=True, default_value=ChildType())
p1prop2 = graphene.Field(ChildType, required=True, default_value=ChildType())
def enable_children(self):
self.p1prop1.enable()
self.p1prop2.enable()

ParentType2(graphene.ObjectType):
p2prop1 = graphene.Field(ParentType, required=True, default_value=ParentType())
p2prop2 = graphene.Field(ParentType, required=True, default_value=ParentType())
```
In schema we call
```
class Query(graphene.ObjectType):
parent_two = graphene.Field(ParentType2)

def resolve_parent_two(self, info, *args, **kwargs):
parent_two = ParentType2()
parent_two.p2prop1.enable_children()
return parent_two
```

When querying parent_two trough localhost:8000/graphql, it returns:
```
parent_two {
p2prop1{
p1prop1{
enabled: True
}
p1prop2{
enabled: True
}
}
p2prop2{
p1prop1{
enabled: True
}
p1prop2{
enabled: True
}
}
}
```
The memory addresses of p2prop1 and p2prop2 are not equal which is right, but somehow mutating p2prop1.p1prop1 also affects to p2prop2.p1prop1 which leads to conclusion that calling constructor ParentType() when setting ParentType2 fields default_value leads to shallow copy instead of deep copy

* **What is the expected behavior?**
When querying parent_two trough localhost:8000/graphql, we expect it return:
```
parent_two {
p2prop1{
p1prop1{
enabled: True
}
p1prop2{
enabled: True
}
}
p2prop2{
p1prop1{
enabled: False
}
p1prop2{
enabled: False
}
}
}
```
We would want ParentType() constructor always make new object instead of shallow copy

* **What is the motivation / use case for changing the behavior?**
Current behaviour leads to unintuitive results

* **Please tell us about your environment:**

- Version:
graphene==2.1.8
graphene-django==2.10.1
- Platform:
Ubuntu 18.04, Pycharm, community edition, Chromium browser

* **Other information** (e.g. detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow)
We can fix the behaviour by calling

```
import copy

parentTypeInstance = ParentType()

ParentType2(graphene.ObjectType):
p2prop1 = graphene.Field(ParentType, required=True, default_value=copy.deepcopy(parentTypeInstance))
p2prop2 = graphene.Field(ParentType, required=True, default_value=copy.deepcopy(parentTypeInstance))
```

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Hướng nghiên cứu

Tái hiện ví dụ lồng nhau về ParentType2, ParentType và ChildType, đồng thời kiểm tra hàm khởi tạo ObjectType và cách xử lý default_value của Field. Xác nhận nơi các instance mặc định lồng nhau trở thành dùng chung, sau đó bổ sung coverage cho thấy việc thay đổi p2prop1 không ảnh hưởng đến p2prop2; hoàn tất khi kết quả GraphQL mong đợi được trả về mà không yêu cầu phía caller phải dùng deepcopy.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
python
Lĩnh vực
api, backend
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
35/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.