graphql-python / graphql-python/graphene

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

オープン
#1,256 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
🐛 bug
主要言語
Python
スター
8.2k
フォーク
818
PR マージ指標
30日以内にマージされた PR はありません

説明

**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))
```

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

Reproduce the nested ParentType2, ParentType, and ChildType example and inspect the ObjectType constructor and Field default_value handling. Confirm where nested default instances become shared, then add coverage showing that mutating p2prop1 does not affect p2prop2; done when the expected GraphQL result is returned without requiring caller-side deepcopy.

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
api, backend
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
35/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。