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

[Feature] Added implement_for decorator #618

Merged
merged 6 commits into from
Nov 2, 2022

Conversation

ordinskiy
Copy link
Contributor

Description

Added the decorator that allows to specify with which version of a given module a function is compatible with. Fitting implementation is selected basing on installed version of the module.

Motivation and Context

It is required for bc-braking packages (e.g. "gym"), so we can have several implementations of the same function for different versions of a module. Proposed by @vmoens.

Types of changes

What types of changes does your code introduce? Remove all that do not apply:

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds core functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation (update in the documentation)
  • Example (update in the folder of examples)

Checklist

Go over all the following points, and put an x in all the boxes that apply.
If you are unsure about any of these, don't hesitate to ask. We are here to help!

  • I have read the CONTRIBUTION guide (required)
  • My change requires a change to the documentation.
  • I have updated the tests accordingly (required for a bug fix or a new feature).
  • I have updated the documentation accordingly.

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 27, 2022
@ordinskiy
Copy link
Contributor Author

ordinskiy commented Oct 27, 2022

@vmoens I have a question about the following case: when installed version of the module doesn't fit any implementation.
For example: we have gym 0.26 and 2 functions with implement_for('gym, '0', '0.24')and implement_for('gym, '0.24', '.25').
Currently, I implemented the decorator, so it just returns the latest declared implementation, but I thought about more strict behaviour, when decorator returns a function, that raises an error (something like "Installed version of 'gym' is not supported), so when the function (with @implement_for) is called this exception is raised.

Which one is preferable from your perspective?

@vmoens
Copy link
Contributor

vmoens commented Oct 27, 2022

Currently, I implemented the decorator, so it just returns the latest declared implementation, but I thought about more strict behaviour, when decorator returns a function, that raises an error (something like "Installed version of 'gym' is not supported), so when the function (with @implement_for) is called this exception is raised.

One option could be that if we use @implemented_for we must cover all versions:

@implemented_for("gym", None, "0.13")
def fun():
   foo()
@implemented_for("gym", "0.13", None)
def fun():
   foo()

is valid but

@implemented_for("gym", None, "0.13")
def fun():
   foo()
@implemented_for("gym", "0.13", "0.14")
def fun():
   foo()

will return an exception during import.

There are caveats with this (it's an optimistic view on future releases), but at least if nothing is expected to break with a new release our CI will just pass. If something breaks, it means we need one more function implementation.

What do you think?

@ordinskiy
Copy link
Contributor Author

With what I suggested as more strict option there won't be an import time exception. The decorated function will become something like:

def fun():
   raise NotImplementedError("Installed version of 'gym' is not supported")

So exception will be thrown only if fun() is actually called, despite the unsupported version, so it should not break CI if the test is skipped. I was thinking to do the same for missing module.

Such strict behaviour is not typical for python and forcing user to implement "extra" functions might not be a good idea, that is why I decided to implement it like I did. Unless, you think that catching the situation when function is called with unsupported (potentially) version of a module beforehand is more often beneficial than inconvenient.

Also, I just realised that I haven't considered "from_version" as None for some reason, but it does look reasonable to allow it, I will do it.

@vmoens vmoens added the enhancement New feature or request label Oct 28, 2022
@codecov
Copy link

codecov bot commented Oct 28, 2022

Codecov Report

Merging #618 (37c481d) into main (dd9a98f) will decrease coverage by 0.09%.
The diff coverage is 90.47%.

@@            Coverage Diff             @@
##             main     #618      +/-   ##
==========================================
- Coverage   87.92%   87.82%   -0.10%     
==========================================
  Files         125      125              
  Lines       24218    24280      +62     
==========================================
+ Hits        21293    21324      +31     
- Misses       2925     2956      +31     
Flag Coverage Δ
habitat-gpu 23.95% <25.92%> (-5.22%) ⬇️
linux-cpu 85.11% <90.47%> (+0.01%) ⬆️
linux-gpu 86.43% <90.47%> (+<0.01%) ⬆️
linux-outdeps-gpu 75.69% <90.47%> (+0.03%) ⬆️
linux-stable-cpu 85.00% <90.47%> (+0.01%) ⬆️
linux-stable-gpu 86.31% <90.47%> (+0.01%) ⬆️
macos-cpu 84.89% <90.47%> (+0.01%) ⬆️
olddeps-gpu 76.54% <90.47%> (+0.03%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
test/test_utils.py 91.89% <86.11%> (-5.55%) ⬇️
torchrl/_utils.py 77.41% <96.15%> (+4.97%) ⬆️
test/_utils_internal.py 67.85% <100.00%> (-19.42%) ⬇️
torchrl/envs/libs/habitat.py 77.77% <0.00%> (-22.23%) ⬇️
torchrl/envs/libs/gym.py 80.71% <0.00%> (-3.05%) ⬇️
test/test_libs.py 95.67% <0.00%> (-2.71%) ⬇️
torchrl/envs/utils.py 95.00% <0.00%> (-1.67%) ⬇️

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@vmoens
Copy link
Contributor

vmoens commented Oct 28, 2022

So exception will be thrown only if fun() is actually called, despite the unsupported version, so it should not break CI if the test is skipped. I was thinking to do the same for missing module.

Such strict behaviour is not typical for python and forcing user to implement "extra" functions might not be a good idea, that is why I decided to implement it like I did. Unless, you think that catching the situation when function is called with unsupported (potentially) version of a module beforehand is more often beneficial than inconvenient.

My suggestion was a bit in the spirit of @abc.abstractmethod where you get an error at loading time if something is not implemented. But an error during execution is equally fine!

@ordinskiy
Copy link
Contributor Author

So exception will be thrown only if fun() is actually called, despite the unsupported version, so it should not break CI if the test is skipped. I was thinking to do the same for missing module.
Such strict behaviour is not typical for python and forcing user to implement "extra" functions might not be a good idea, that is why I decided to implement it like I did. Unless, you think that catching the situation when function is called with unsupported (potentially) version of a module beforehand is more often beneficial than inconvenient.

My suggestion was a bit in the spirit of @abc.abstractmethod where you get an error at loading time if something is not implemented. But an error during execution is equally fine!

Alright, I will change the behaviour, so the decorator will replace the function with a mock that raises the exception by default (until fitting implementation found).
Plus, I missed the case when two functions in different modules have the same name, I will fix it.

Copy link
Contributor

@vmoens vmoens left a comment

Choose a reason for hiding this comment

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

Just a couple of minor fixes and we're good to go!
Thanks for this!

test/test_utils.py Outdated Show resolved Hide resolved
test/test_utils.py Outdated Show resolved Hide resolved
test/test_utils.py Outdated Show resolved Hide resolved
torchrl/_utils.py Show resolved Hide resolved
@vmoens vmoens merged commit e96fd37 into pytorch:main Nov 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants