Role permission resolver
Your task
An admin console hands every teammate a set of roles, and roles borrow from one another. Each entry of inherits is written role:base and means that role picks up everything base has, including whatever base borrowed in turn. Each entry of grants is written role=permission and gives that role one permission directly. Role names never contain a colon or an equals sign, and permission names never contain an equals sign. The console does not check the borrowing for loops, so a chain can come back on itself and the resolver still has to finish. Given inherits, grants and member_roles, return every distinct permission the teammate ends up holding, sorted in ascending order. A role nobody granted anything to contributes nothing, and a role named in member_roles that appears nowhere else contributes nothing. Return an empty list when the teammate holds no permissions.
Examples
effective_permission_set(inherits: list[str], grants: list[str], member_roles: list[str]) -> list[str]effective_permission_set(["support-lead:support", "admin:support-lead"], ["support=ticket.read", "support-lead=ticket.close", "admin=user.delete"], ["admin"])["ticket.close", "ticket.read", "user.delete"]effective_permission_set(["analyst:viewer"], ["viewer=report.read", "analyst=report.export"], ["viewer"])["report.read"]effective_permission_set(["lead:eng", "lead:oncall"], ["eng=repo.read", "oncall=page.ack", "lead=repo.read"], ["lead"])["page.ack", "repo.read"]