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

feat: adds base class definition for resources #83

Merged
merged 1 commit into from
Mar 13, 2024

Conversation

tdstein
Copy link
Collaborator

@tdstein tdstein commented Mar 6, 2024

Adding an ABC class to enforce the style choices defined in ./src/connect/README.md

Copy link

github-actions bot commented Mar 6, 2024

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
318 276 87% 80% 🟢

New Files

File Coverage Status
src/posit/connect/resources.py 100% 🟢
TOTAL 100% 🟢

Modified Files

File Coverage Status
src/posit/connect/content.py 91% 🟢
src/posit/connect/users.py 94% 🟢
TOTAL 92% 🟢

updated for commit: b88bf12 by action🐍

@tdstein tdstein force-pushed the tdstein/resources-interface branch from 0a57800 to b88bf12 Compare March 6, 2024 21:47
@tdstein tdstein marked this pull request as ready for review March 6, 2024 21:48
raise NotImplementedError()

@abstractmethod
def delete(self, *args, **kwargs) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does delete() make sense for these collections? Would this be a bulk delete action?

Copy link
Collaborator Author

@tdstein tdstein Mar 12, 2024

Choose a reason for hiding this comment

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

Based on this method definition, the implementation can do either. We haven't tried injecting the session object into the associated Resource yet. This should be solved as part of #23.

Typically, I would expect this method to act on a collection, which would either be the syntax sugar of user.delete() for user in users (pseudo-code) or a direct call to a DELETE endpoint that supports deleting multiple items in a collection.

raise NotImplementedError()

@abstractmethod
def get(self, *args, **kwargs) -> T:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should id/guid be a part of the get() signature?

Copy link
Collaborator Author

@tdstein tdstein Mar 8, 2024

Choose a reason for hiding this comment

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

Maybe? I assume that certain implementations may need to use a composite key for item references.

@@ -25,7 +27,7 @@ class User(TypedDict, total=False):
locked: bool


class Users:
class Users(Resources[User]):
Copy link
Collaborator

Choose a reason for hiding this comment

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

What use is it to have Resources[User] vs. a simple Resources base class? Can/should you use the T type in the method definitions? Do you need to define the return types for the methods, or are they redundant to the base class?

More meta question: supposing you could factor some of those things out of the method definitions, should you? I could see an advantage of the current version being that it is explicit (famously better than implicit in Python ;), that you can read the class definition and see what it's doing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not sure I follow. There may be something I need to learn about generics in Python.

Using a parametrized type in the ABC enforces that the inheriting class has consistent return types across methods. Are you suggesting that there are scenarios where this won't be the case?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess one place I'm confused is that we're still defining the return types in this subclass (e.g. L38, find() returns List[User]). Can/should those type annotations be removed because the base class will handle them for you?

My other question is: for example, on L40, we call User(**user), can/should that be T(**user)?

The other way I've seen this done is that you would set self._resource_type = User or something and use that to construct the objects. (Not saying that's worth doing necessarily.)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In this case, consider Resources an interface instead of an abstract class since it only has abstract methods. If Resources contained non-abstract methods, then T(**user) should be the correct usage for conversion in those methods (although it would be T(**result)).

I believe self._resource_type = User pattern was correct before generics were introduced in Python. This would have been the only way to inform the base class of the implementing type.

Can/should those type annotations be removed because the base class will handle them for you?

They can, but when writing the implementing code, the Python language server will auto-complete with the correct type definitions.

raise NotImplementedError()

@abstractmethod
def update(self, *args, **kwargs) -> T:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same question as delete: would this be for bulk updates (regardless of whether the APIs would force us to do one request per entity)?

@tdstein tdstein merged commit 6657186 into main Mar 13, 2024
8 checks passed
@tdstein tdstein deleted the tdstein/resources-interface branch March 13, 2024 14:13
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.

2 participants