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

Fix bug with str(OAuthDependentTokenResponse) #644

Merged
merged 2 commits into from
Nov 7, 2022

Conversation

sirosen
Copy link
Member

@sirosen sirosen commented Nov 4, 2022

The stringifier inherited from OAuthTokenResponse assumes that self.get(...) is safe, but that's only true when the data is a dict, as it normally is.
OAuthDependentTokenResponse has self.data: list. Therefore, this needs a check and special handling in the stringifier.

Add a test case which fails without this addition, then make the get() call conditional on the type of the underlying data.

Arguably this is a "bad" division of responsibilities (base class knows about its subclasses), but any alternative which solves this generically is more verbose or touches on core codepaths like the central GlobusHTTPResponse.get implementation.

The stringifier inherited from OAuthTokenResponse assumes that
`self.get(...)` is safe, but that's only true when the data is a
`dict`, as it normally is.
OAuthDependentTokenResponse has `self.data: list`. Therefore, this
needs a check and special handling in the stringifier.

Add a test case which fails without this addition, then make the
`get()` call conditional on the type of the underlying data.

Arguably this is a "bad" division of responsibilities (base class
knows about its subclasses), but any alternative which solves this
generically is more verbose or touches on core codepaths like the
central `GlobusHTTPResponse.get` implementation.
@sirosen sirosen added bug Something isn't working correctly no-news-is-good-news This change does not require a news file labels Nov 4, 2022
Comment on lines 231 to 232
if isinstance(self.data, dict):
id_token_to_print = t.cast(t.Optional[str], self.get("id_token"))
Copy link
Contributor

@derek-globus derek-globus Nov 4, 2022

Choose a reason for hiding this comment

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

It feels boundary breaking to have this check self.data but then retrieve a value by calling self.get(...)

Could we either

  1. If we're comfortable that sometimes data in OAuthTokenResponse won't be a dict, make .get safe for OAuthTokenResponse. Maybe override the get method like
    class OAuthTokenReponse(GlobusHTTPResponse):
         ...
         def __getitem__(self, key: t.Union[str, int, slice])) -> t.Any:
             if not isinstance(self.data, dict):
                return None
            return super().__getitem__(key)
         ...
    
  2. Or check the same thing we're querying from:
    1. Move self.get(...) to self.data.get(...) so that we're querying the same object we just verified, not relying on the fact that the internals of __getitem__ do that
    2. I guess error handling would be the way to handle the "not-a-dict" option from the response interface level
      try:
           id_token_to_print = t.cast(t.Optional[str], self.get("id_token"))
      except ValueError:
           id_token_to_print = None
      

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for identify this -- I didn't love it either but didn't have better ideas at the time. Why don't we go with redefining get on this one class to handle non-dict data? I'll put together a version of that later.

Copy link
Member Author

Choose a reason for hiding this comment

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

I opened this up in order to make the change today, and I realized that there's very little reason for GlobusHTTPResponse.get not to handle list data nicely.

I've gone ahead and added a commit which moves the check to the base class' get method, and removes this check here entirely. It also adds an appropriate changelog item.

It's a pretty significant change in where the fix is applied, and it's breadth of potential impact and utility. But I have started to think that it's the best way to fix this problem.

`GlobusHTTPResponse.get` checks if the data is `None` for special
behavior, but this check can easily be extended to the case in which
`data` is a list. This makes `get()` safer to use when data may be
a dict or list, as it won't try to user `list.get` and encounter an
AttributeError.

Add a changelog fragment to this effect.

The result is also that the changes to `OAuthTokenResponse` to handle
the `OauthDependentTokenResponse` are no longer necessary and are
reverted here.
Copy link
Contributor

@aaschaer aaschaer left a comment

Choose a reason for hiding this comment

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

Looks good to me!

@sirosen sirosen merged commit f3c153d into globus:main Nov 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working correctly no-news-is-good-news This change does not require a news file
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants