Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-38293: Allow shallow and deep copying of property objects #16438

Merged
merged 2 commits into from Jan 12, 2020

Conversation

@GudniNatan
Copy link
Contributor

GudniNatan commented Sep 27, 2019

Copying property objects results in a TypeError. Steps to reproduce:

>>> import copy
>>> obj = property()
>>> copy.copy(obj)

This affects both shallow and deep copying.
My idea for a fix is to add property objects to the list of "atomic" objects in the copy module.
These already include types like functions and type objects.

I also added property objects to the unit tests test_copy_atomic and test_deepcopy_atomic. This is my first PR, and it's highly likely I've made some mistake, so please be kind :)

https://bugs.python.org/issue38293

Automerge-Triggered-By: @csabella

Copy link
Contributor

eamanu left a comment

Good catch @GudniNatan i am not copy expert but I test it and run successfully.

I don't know if will need necessary a NEWS entry, IMO yes, but I would like hear some
core dev opinion.

Copy link
Member

brandtbucher left a comment

Thanks @GudniNatan, and welcome to CPython. This looks good!

I think this should have a NEWS entry. It's a new feature (and there's precedent for it when copy/deepcopy support was added for other objects in the past). Creating one is super quick!

Maybe just something like:

Add :func:`copy.copy` and :func:`copy.deepcopy` support to :func:`property` objects.

Just out of curiosity, what was your use case for wanting to copy these?

@GudniNatan

This comment has been minimized.

Copy link
Contributor Author

GudniNatan commented Sep 29, 2019

I was experimenting with using property objects to validate data in dataclasses. Here is some code that should help illustrate:

from dataclasses import field, dataclass, asdict, fields


def string_check(x):
    if type(x) != str:
        raise ValueError("Should be a string.")
    return x


def validator_factory(field_name, validator):
    def fget(self):
        return self.__dict__[field_name]

    def fset(self, value):
        self.__dict__[field_name] = validator(value)

    return property(fget, fset)


@dataclass
class MyClass():
    my_value: str = validator_factory("my_value", string_check)


@dataclass
class ClassSchema():
    field_defaults: list


my_class_schema = ClassSchema(
    field_defaults=[f.default for f in fields(MyClass)]
)

print(my_class_schema)
print(asdict(my_class_schema))

The MyClass dataclass has an attribute my_value, and the idea is that if you set it to be anything other than a string, it will throw a ValueError.

We were also using a library (dataclasses-jsonschema) that has some similar functionality to ClassSchema, in that it stores the default values of our dataclasses. It will then try to convert those to dictionaries with the dataclasses asdict function. However, asdict will try to give you a dictionary that is independent of the instance you feed it. To do that it uses the deepcopy, and then you get the TypeError: can't pickle property objects

So basically calling asdict() on certain dataclass instances results in this weird error.

Copy link
Member

gvanrossum left a comment

I have a nagging doubt. You can create a property from any callable, e.g. property(lambda self: None). The argument is not type-checked, so you can also create a property from a non-callable, e.g. property([1, 2, 3]). When we deep-copy such a property, shouldn't we deep-copy the list (i.e. the fget attribute)? After all, when we deep-copy a tuple containing a list, the list gets copied.

@GudniNatan For your use case, is deep-copyability required?

@arigo

This comment has been minimized.

Copy link
Contributor

arigo commented Oct 23, 2019

@gvanrossum following that argument, copy.copy(func) should not return its original object either, because func is mutable (i.e. a following func.foo = 42 should not appear on its copy). Of course I'm not suggesting that this should be changed; this is only a reminder that copy() and deepcopy() are best-effort hacks in the first place.

@gvanrossum

This comment has been minimized.

Copy link
Member

gvanrossum commented Oct 23, 2019

@arigo That's fair. Tuples are special, because they are meant to be robust data structures, and a tuple containing a list is a well-known (and used) corner case. I'll approve.

@miss-islington miss-islington merged commit 9f3fc6c into python:master Jan 12, 2020
4 checks passed
4 checks passed
Azure Pipelines PR #20190929.5 succeeded
Details
bedevere/issue-number Issue number 38293 found
Details
bedevere/news News entry found in Misc/NEWS.d
continuous-integration/travis-ci/pr The Travis CI build passed
Details
@miss-islington

This comment has been minimized.

Copy link

miss-islington commented Jan 12, 2020

Thanks @GudniNatan for the PR 🌮🎉.. I'm working now to backport this PR to: 3.7, 3.8.
🐍🍒🤖

@miss-islington

This comment has been minimized.

Copy link

miss-islington commented Jan 12, 2020

Sorry @GudniNatan, I had trouble checking out the 3.8 backport branch.
Please backport using cherry_picker on command line.
cherry_picker 9f3fc6c5b4993f2b362263b494f84793a21aa073 3.8

@miss-islington miss-islington self-assigned this Jan 12, 2020
miss-islington added a commit to miss-islington/cpython that referenced this pull request Jan 12, 2020
…GH-16438)

Copying property objects results in a TypeError. Steps to reproduce:

```
>>> import copy
>>> obj = property()
>>> copy.copy(obj)
````

This affects both shallow and deep copying.
My idea for a fix is to add property objects to the list of "atomic" objects in the copy module.
These already include types like functions and type objects.

I also added property objects to the unit tests test_copy_atomic and test_deepcopy_atomic. This is my first PR, and it's highly likely I've made some mistake, so please be kind :)

https://bugs.python.org/issue38293
(cherry picked from commit 9f3fc6c)

Co-authored-by: Guðni Natan Gunnarsson <1493259+GudniNatan@users.noreply.github.com>
@bedevere-bot

This comment has been minimized.

Copy link

bedevere-bot commented Jan 12, 2020

GH-17968 is a backport of this pull request to the 3.7 branch.

@miss-islington

This comment has been minimized.

Copy link

miss-islington commented Jan 12, 2020

Thanks @GudniNatan for the PR 🌮🎉.. I'm working now to backport this PR to: 3.8.
🐍🍒🤖 I'm not a witch! I'm not a witch!

@bedevere-bot

This comment has been minimized.

Copy link

bedevere-bot commented Jan 12, 2020

GH-17969 is a backport of this pull request to the 3.8 branch.

miss-islington added a commit to miss-islington/cpython that referenced this pull request Jan 12, 2020
…GH-16438)

Copying property objects results in a TypeError. Steps to reproduce:

```
>>> import copy
>>> obj = property()
>>> copy.copy(obj)
````

This affects both shallow and deep copying.
My idea for a fix is to add property objects to the list of "atomic" objects in the copy module.
These already include types like functions and type objects.

I also added property objects to the unit tests test_copy_atomic and test_deepcopy_atomic. This is my first PR, and it's highly likely I've made some mistake, so please be kind :)

https://bugs.python.org/issue38293
(cherry picked from commit 9f3fc6c)

Co-authored-by: Guðni Natan Gunnarsson <1493259+GudniNatan@users.noreply.github.com>
miss-islington added a commit that referenced this pull request Jan 12, 2020
Copying property objects results in a TypeError. Steps to reproduce:

```
>>> import copy
>>> obj = property()
>>> copy.copy(obj)
````

This affects both shallow and deep copying.
My idea for a fix is to add property objects to the list of "atomic" objects in the copy module.
These already include types like functions and type objects.

I also added property objects to the unit tests test_copy_atomic and test_deepcopy_atomic. This is my first PR, and it's highly likely I've made some mistake, so please be kind :)

https://bugs.python.org/issue38293
(cherry picked from commit 9f3fc6c)

Co-authored-by: Guðni Natan Gunnarsson <1493259+GudniNatan@users.noreply.github.com>
sthagen added a commit to sthagen/cpython that referenced this pull request Jan 12, 2020
bpo-38293: Allow shallow and deep copying of property objects (pythonGH-16438)
miss-islington added a commit that referenced this pull request Jan 12, 2020
Copying property objects results in a TypeError. Steps to reproduce:

```
>>> import copy
>>> obj = property()
>>> copy.copy(obj)
````

This affects both shallow and deep copying.
My idea for a fix is to add property objects to the list of "atomic" objects in the copy module.
These already include types like functions and type objects.

I also added property objects to the unit tests test_copy_atomic and test_deepcopy_atomic. This is my first PR, and it's highly likely I've made some mistake, so please be kind :)

https://bugs.python.org/issue38293
(cherry picked from commit 9f3fc6c)

Co-authored-by: Guðni Natan Gunnarsson <1493259+GudniNatan@users.noreply.github.com>
@brandtbucher

This comment has been minimized.

Copy link
Member

brandtbucher commented Jan 12, 2020

Congrats on your first CPython contribution @GudniNatan! 🍾

Looking forward to seeing more from you in the future.

petdance added a commit to petdance/cpython that referenced this pull request Jan 17, 2020
…GH-16438)

Copying property objects results in a TypeError. Steps to reproduce:

```
>>> import copy
>>> obj = property()
>>> copy.copy(obj)
````

This affects both shallow and deep copying.  
My idea for a fix is to add property objects to the list of "atomic" objects in the copy module.
These already include types like functions and type objects.

I also added property objects to the unit tests test_copy_atomic and test_deepcopy_atomic. This is my first PR, and it's highly likely I've made some mistake, so please be kind :)


https://bugs.python.org/issue38293
petdance added a commit to petdance/cpython that referenced this pull request Jan 17, 2020
…GH-16438)

Copying property objects results in a TypeError. Steps to reproduce:

```
>>> import copy
>>> obj = property()
>>> copy.copy(obj)
````

This affects both shallow and deep copying.  
My idea for a fix is to add property objects to the list of "atomic" objects in the copy module.
These already include types like functions and type objects.

I also added property objects to the unit tests test_copy_atomic and test_deepcopy_atomic. This is my first PR, and it's highly likely I've made some mistake, so please be kind :)


https://bugs.python.org/issue38293
petdance added a commit to petdance/cpython that referenced this pull request Jan 17, 2020
…GH-16438)

Copying property objects results in a TypeError. Steps to reproduce:

```
>>> import copy
>>> obj = property()
>>> copy.copy(obj)
````

This affects both shallow and deep copying.  
My idea for a fix is to add property objects to the list of "atomic" objects in the copy module.
These already include types like functions and type objects.

I also added property objects to the unit tests test_copy_atomic and test_deepcopy_atomic. This is my first PR, and it's highly likely I've made some mistake, so please be kind :)


https://bugs.python.org/issue38293
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.