Skip to content

[Utils] Add deprecate function and move testing_utils under utils #659

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

Merged
merged 18 commits into from
Oct 3, 2022

Conversation

patrickvonplaten
Copy link
Contributor

@patrickvonplaten patrickvonplaten commented Sep 27, 2022

Improved deprecation functionality

We are introducing multiple deprecation cycles already and currently are missing structure on how to handle this exactly and by adding **kwargs to the scheduler config init we have lost functionality. More specifically:

    1. We do not keep track when functionality should be removed as we forget to remove the deprecation cycle and the functionality (this happened twice now)
    1. Right now when you pass a incorrect argument, e.g. PNDMScheduler(num_inference_steps=5) nothing happens because it's swallowed by **kwargs. However we should, just as before [Pytorch] add dep. warning for pytorch schedulers #651 throw a TypeError in this case.

=> This PR solves this by introducing a deprecate function (which should be mainly used by maintainers) that automatically forces us to remove deprecated functionality correctly + solves 2. by throwing a type error. In addition it removes boiler plate code and should make it more efficient for us to deprecate things.

The API of deprecate is as follows:

new_arg = deprecate("old_arg", "0.7.0", "Please use `new_arg` instead", take_from=kwargs) or new_arg

=> This forces us to remove this statement when version >= 0.7.0, gives a nice default error message and makes sure that kwargs can only have deprecated args.

Please take a look into the changes here and the tests to better understand all the functionality

@HuggingFaceDocBuilderDev
Copy link

HuggingFaceDocBuilderDev commented Sep 27, 2022

The documentation is not available anymore as the PR was closed or merged.

@patrickvonplaten patrickvonplaten changed the title [Utils] Add deprecate function [Utils] Add deprecate function and move testing_utils under utils Sep 29, 2022
)
deprecate("steps_offset!=1", "1.0.0", deprecation_message, standard_warn=False)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a big change and I like to keep it for a bit until we force people to not pass steps_offset anymore.


offset = kwargs["offset"]
deprecated_offset = deprecate(
"offset", "0.5.0", "Please pass `steps_offset` to `__init__` instead.", take_from=kwargs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bump as we would have already missed this version


offset = kwargs["offset"]
deprecated_offset = deprecate(
"offset", "0.5.0", "Please pass `steps_offset` to `__init__` instead.", take_from=kwargs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bump as we would have already missed this version

" `'images'` instead.",
DeprecationWarning,
)
deprecate("samples", "0.6.0", "Please use `.images` or `'images'` instead.")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bump twice as we would have already missed this version and it's important

from diffusers.utils import deprecate


class DeprecateTester(unittest.TestCase):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test best describes all the functionality that we need to ensure

Copy link
Contributor

@patil-suraj patil-suraj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for working on this, this will be very useful!
And really nice tests 😍 ! Love this way of explaining new features with tests.

Comment on lines 27 to 65
def deprecate(*args, take_from: Optional[Union[Dict, Any]] = None, standard_warn=True):
from .. import __version__

deprecated_kwargs = take_from
values = ()
if not isinstance(args[0], tuple):
args = (args,)

for attribute, version_name, message in args:
if version.parse(version.parse(__version__).base_version) >= version.parse(version_name):
raise ValueError(
f"The deprecation tuple {(attribute, version_name, message)} should be removed since diffusers'"
f" version {__version__} is >= {version_name}"
)

warning = None
if isinstance(deprecated_kwargs, dict) and attribute in deprecated_kwargs:
values += (deprecated_kwargs.pop(attribute),)
warning = f"The `{attribute}` argument is deprecated and will be removed in version {version_name}."
elif hasattr(deprecated_kwargs, attribute):
values += (getattr(deprecated_kwargs, attribute),)
warning = f"The `{attribute}` attribute is deprecated and will be removed in version {version_name}."
elif deprecated_kwargs is None:
warning = f"`{attribute}` is deprecated and will be removed in version {version_name}."

if warning is not None:
warning = warning + " " if standard_warn else ""
warnings.warn(warning + message, DeprecationWarning)

if isinstance(deprecated_kwargs, dict) and len(deprecated_kwargs) > 0:
call_frame = inspect.getouterframes(inspect.currentframe())[1]
filename = call_frame.filename
line_number = call_frame.lineno
function = call_frame.function
key, value = next(iter(deprecated_kwargs.items()))
raise TypeError(f"{function} in {filename} line {line_number-1} got an unexpected keyword argument `{key}`")

if len(values) == 0:
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Big +1

Copy link
Member

@pcuenca pcuenca left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is extremely cool, and so useful! It was very helpful to start by reading the tests.

Seeing that deprecate was inside testing_utils.py sounded a bit off, so I looked more closely to see what was there and realized that we would be breaking import diffusers when PyTorch is not installed. In any case, I believe that the semantics of the function call for moving it to another place.

deprecated_offset = deprecate(
"offset", "0.5.0", "Please pass `steps_offset` to `__init__` instead.", take_from=kwargs
)
offset = deprecated_offset or self.config.steps_offset
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great naming here, the intention is very clear!

Comment on lines 27 to 65
def deprecate(*args, take_from: Optional[Union[Dict, Any]] = None, standard_warn=True):
from .. import __version__

deprecated_kwargs = take_from
values = ()
if not isinstance(args[0], tuple):
args = (args,)

for attribute, version_name, message in args:
if version.parse(version.parse(__version__).base_version) >= version.parse(version_name):
raise ValueError(
f"The deprecation tuple {(attribute, version_name, message)} should be removed since diffusers'"
f" version {__version__} is >= {version_name}"
)

warning = None
if isinstance(deprecated_kwargs, dict) and attribute in deprecated_kwargs:
values += (deprecated_kwargs.pop(attribute),)
warning = f"The `{attribute}` argument is deprecated and will be removed in version {version_name}."
elif hasattr(deprecated_kwargs, attribute):
values += (getattr(deprecated_kwargs, attribute),)
warning = f"The `{attribute}` attribute is deprecated and will be removed in version {version_name}."
elif deprecated_kwargs is None:
warning = f"`{attribute}` is deprecated and will be removed in version {version_name}."

if warning is not None:
warning = warning + " " if standard_warn else ""
warnings.warn(warning + message, DeprecationWarning)

if isinstance(deprecated_kwargs, dict) and len(deprecated_kwargs) > 0:
call_frame = inspect.getouterframes(inspect.currentframe())[1]
filename = call_frame.filename
line_number = call_frame.lineno
function = call_frame.function
key, value = next(iter(deprecated_kwargs.items()))
raise TypeError(f"{function} in {filename} line {line_number-1} got an unexpected keyword argument `{key}`")

if len(values) == 0:
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Big +1

from distutils.util import strtobool
from pathlib import Path
from typing import Union
from typing import Any, Dict, Optional, Union

import torch
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this breaks import diffusers if PyTorch is not installed.

We should move deprecate to its own file. Not sure whether we should call it deprecate.py, deprecation_utils.py or something more general in case we need more functionality in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good! I'll add it to deprecation_utils.py

Regarding import torch, this should be done in another PR - this is unrelated to this PR (import torch was there before). We should definitely make sure though that the library doesn't break when torch is not installed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding import torch, this should be done in another PR - this is unrelated to this PR (import torch was there before).

Yes, but testing_utils was only imported when testing, that's really the difference (not the import). I should have made it clearer in my comment :)

@patrickvonplaten patrickvonplaten merged commit f1484b8 into main Oct 3, 2022
@patrickvonplaten patrickvonplaten deleted the add_deprecate_function branch October 3, 2022 21:44
prathikr pushed a commit to prathikr/diffusers that referenced this pull request Oct 26, 2022
…ggingface#659)

* [Utils] Add deprecate function

* up

* up

* uP

* up

* up

* up

* up

* uP

* up

* fix

* up

* move to deprecation utils file

* fix

* fix

* fix more
PhaneeshB pushed a commit to nod-ai/diffusers that referenced this pull request Mar 1, 2023
…ace#659)

(This goes on to produce compilation errors, but one step at a time)
yoonseokjin pushed a commit to yoonseokjin/diffusers that referenced this pull request Dec 25, 2023
…ggingface#659)

* [Utils] Add deprecate function

* up

* up

* uP

* up

* up

* up

* up

* uP

* up

* fix

* up

* move to deprecation utils file

* fix

* fix

* fix more
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants