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

Add support for static deletion #543

Merged
merged 2 commits into from
Apr 3, 2019
Merged

Add support for static deletion #543

merged 2 commits into from
Apr 3, 2019

Conversation

ob-stripe
Copy link
Contributor

@ob-stripe ob-stripe commented Mar 17, 2019

r? @brandur-stripe @remi-stripe

A very hacky and experimental way of adding support for static deletion on deletable resource classes.

Normally, Python doesn't allow you to have both a class and instance method with the same name. This PR works around this limitation by defining a special class_or_instance_method decorator. The decorator is defined as a non-data descriptor. Descriptors receive both the instance and the class as arguments in their __get__ method. The method checks if the instance variable (obj) is None or not to decide whether the method should be called as a class method (with the type variable objtype as the first argument) or as an instance method (with the instance variable obj as the first argument).

The actual "class or instance method" can then check if its first argument is a type or not to decide whether it was called from a class or from an instance and behave appropriately.

Tagged as WIP because this is rather hacky and I want to get your thoughts on it first. If we're comfortable with this approach, I'll add some safeguards around arguments (right now if you call e.g. stripe.Customer.delete() without any arguments, you get a very unclear "tuple index out of range" error because the code tries to access args[0] to get the ID) and write additional tests.

@remi-stripe
Copy link
Contributor

Damn, I said it could not be done in Python and obviously you immediately show it can. This is really cool but I have no idea how safe it is to take that approach, especially as we'd have to do this for all the instance methods in the lib (though there are not that many beyond retrieve).

How would it work for nested resources by the way? In the sense of trying to have delete static on Person, versus having to do Account.deletePerson() be static (though that could work too).

I'll defer to @brandur-stripe on whether this is the wrong approach versus just doing a new major revision that only has static methods.

@remi-stripe remi-stripe removed their assignment Mar 17, 2019
@ob-stripe
Copy link
Contributor Author

we'd have to do this for all the instance methods in the lib (though there are not that many beyond retrieve).

retrieve is already static ;) and we already have modify for update requests, so I think that leaves only delete and all non-CRUDL instance methods (capture, etc.).

How would it work for nested resources by the way? In the sense of trying to have delete static on Person, versus having to do Account.deletePerson() be static (though that could work too).

Classes for parent resources already have class methods for all nested resources operations, e.g. Account.delete_person(acct_id, person_id). I think it's better this way (in the sense that removing a person from an account is more of an "account operation" than a "person operation"), and it also sidesteps the issue of nested resources with multiple possible parent resources (cards, bank accounts).

@brandur-stripe
Copy link
Contributor

Wow OB, this is some serious Gandalf-level Python wizardry. Nice patch!

So I would probably have said that my first preference would have been to come up with two slightly different method names for the static versus non-static action even if the difference was somewhat arbitrary (destroy versus delete). This seems less likely to cause confusion to me.

That said, I appreciate that "delete" is probably the right word from an API standpoint (as it meshes well with DELETE), and we already have static "delete" functions in languages like Go, so I don't feel too strongly about it. This seems fine to me if you want to go with it.

@ob-stripe ob-stripe force-pushed the ob-delete-class-method branch 2 times, most recently from 8a9adb6 to f04ac92 Compare April 2, 2019 21:47
@ob-stripe
Copy link
Contributor Author

I've updated the PR with a slightly more complicated decorator/descriptor (a desecrator? 😛) but I think the result is really nice.

The new decorator is applied directly on a regular instance method, and takes an alternate class method name as its parameter. When the method is called on the class, then the alternate class method is called, otherwise the regular instance method is called.

ptal @brandur-stripe @remi-stripe

@brandur-stripe
Copy link
Contributor

Sorry, approved a little prematurely, but this approach seems okay to me.

@ob-stripe
Copy link
Contributor Author

NP, thanks Brandur! We're just going to add some more tests before merging.

@remi-stripe
Copy link
Contributor

@ob-stripe I just did all the examples, PTAL

Copy link
Contributor Author

@ob-stripe ob-stripe left a comment

Choose a reason for hiding this comment

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

LGTM

@ob-stripe ob-stripe changed the title [WIP] Add support for static deletion Add support for static deletion Apr 2, 2019
@remi-stripe
Copy link
Contributor

@ob-stripe I force pushed as I had forgotten to do Terminal. Please make sure I did not miss any other

@ob-stripe ob-stripe merged commit 88d2a2b into master Apr 3, 2019
@ob-stripe ob-stripe deleted the ob-delete-class-method branch April 3, 2019 17:29
@ob-stripe
Copy link
Contributor Author

Released as 2.24.0.

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

Successfully merging this pull request may close these issues.

None yet

4 participants