I have a CLI lib to automate batch operations on GitHub with many use cases: create issue, merge PR, delete branch... Since they all operate in a similar fashing regarding execution and output, I created a parent class GhUseCase as follows:

class GhUseCase:
    # ...other methods
    def action(self, github_repo: str, *args: Any, **kwargs: Any) -> None:
        """Execute some action in a repo."""
        raise NotImplementedError  # pragma: no cover

    def execute(
        self, *args: Any, **kwargs: Any
    ) -> Union[res.ResponseFailure, res.ResponseSuccess]:
        """Execute GitHubUseCase."""
        if self.github_repo:
            self.action(self.github_repo, *args, **kwargs)
            for github_repo in self.config_manager.config.github_selected_repos:
                self.action(github_repo, *args, **kwargs)
        return self.generate_response()

Basically, each use cases inherits this class and implements action method. Eg. overload 1 (delete branch):

import git_portfolio.use_cases.gh as gh

class GhDeleteBranchUseCase(gh.GhUseCase):
    """Github delete branch use case."""

    def action(self, github_repo: str, branch: str) -> None:  # type: ignore[override]
        """Delete branches."""
        github_service_method = "delete_branch_from_repo"
        self.call_github_service(github_service_method, github_repo, branch)

Eg. overload 2 (create issue) with another signature:

import git_portfolio.domain.issue as i
import git_portfolio.use_cases.gh as gh

class GhCreateIssueUseCase(gh.GhUseCase):
    """Github create issue use case."""

    def action(  # type: ignore[override]
        self, github_repo: str, issue: i.Issue
    ) -> None:
        """Create issues."""
        github_service_method = "create_issue_from_repo"
        self.call_github_service(github_service_method, github_repo, issue)

Full use-cases code can be found in this branch (files with names stating in gh).

The problem is that each use case's action has a different list of parameters and I could not find a proper way to declare the type annotations without having type: ignore[override] errors on Mypy.

Any ideas of how to make this better and properly annotated?

  • \$\begingroup\$ The current question title, which states your concerns about the code, applies to too many questions on this site to be useful. The site standard is for the title to simply state the task accomplished by the code. Please see How do I ask a good question?. \$\endgroup\$ – BCdotWEB Mar 3 at 14:28
  • \$\begingroup\$ Fair enough, I tried to put a more specific title then. \$\endgroup\$ – staticdev Mar 3 at 15:18
  • \$\begingroup\$ @FMc the problem is that every action needs some specific parameters regarding their use case. In the two examples I put you can see that, delete branch needs the branch name and create issue, all the issue fields. I am not sure I understood your suggestion, you suggest that I create one dataclass or attrs class with all parameters for all actions or what? \$\endgroup\$ – staticdev Mar 9 at 9:09
  • \$\begingroup\$ So if I have 20 use cases with 40 different parameters I create a class with all 50 parameters? I think there should be a more modular solution in that case to make it more maintainable. \$\endgroup\$ – staticdev Mar 11 at 8:42
  • \$\begingroup\$ I don't understand what you are trying to type check. execute() accepts arguments of any type (signature [Any, Any]), so there isn't anything to type check there. And it then uses self.action(*args, **kwargs) to call an instance method, so there isn't any type information to check there either. The types in action() could be used to type check the code in action() itself, but we don't know what args call_github_service() takes. \$\endgroup\$ – RootTwo Mar 23 at 14:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.