Skip to content

Commit 1383535

Browse files
refactor: add __str__ and __repr__ to data classes for better representation
1 parent b7dbf55 commit 1383535

6 files changed

Lines changed: 302 additions & 23 deletions

File tree

openedx_authz/api/data.py

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@ def get_subclass_by_external_key(mcs, external_key: str) -> Type["ScopeData"]:
172172
scope_subclass = mcs.scope_registry.get(namespace)
173173

174174
if not scope_subclass:
175-
raise ValueError(f"Unknown scope: {namespace} for external_key: {external_key}")
175+
raise ValueError(
176+
f"Unknown scope: {namespace} for external_key: {external_key}"
177+
)
176178

177179
if not scope_subclass.validate_external_key(external_key):
178180
raise ValueError(f"Invalid external_key format: {external_key}")
@@ -261,6 +263,14 @@ def validate_external_key(cls, external_key: str) -> bool:
261263
except InvalidKeyError:
262264
return False
263265

266+
def __str__(self):
267+
"""Human readable string representation of the content library."""
268+
return self.library_id
269+
270+
def __repr__(self):
271+
"""Developer friendly string representation of the content library."""
272+
return self.namespaced_key
273+
264274

265275
class SubjectMeta(type):
266276
"""Metaclass for SubjectData to handle dynamic subclass instantiation based on namespace."""
@@ -342,6 +352,14 @@ def username(self) -> str:
342352
"""
343353
return self.external_key
344354

355+
def __str__(self):
356+
"""Human readable string representation of the user."""
357+
return self.username
358+
359+
def __repr__(self):
360+
"""Developer friendly string representation of the user."""
361+
return self.namespaced_key
362+
345363

346364
@define
347365
class ActionData(AuthZData):
@@ -365,9 +383,17 @@ def name(self) -> str:
365383
"""
366384
return self.external_key.replace("_", " ").title()
367385

386+
def __str__(self):
387+
"""Human readable string representation of the action."""
388+
return self.name
389+
390+
def __repr__(self):
391+
"""Developer friendly string representation of the action."""
392+
return self.namespaced_key
393+
368394

369395
@define
370-
class PermissionData(AuthZData):
396+
class PermissionData:
371397
"""A permission is an action that can be performed under certain conditions.
372398
373399
Attributes:
@@ -377,6 +403,14 @@ class PermissionData(AuthZData):
377403
action: ActionData = None
378404
effect: Literal["allow", "deny"] = "allow"
379405

406+
def __str__(self):
407+
"""Human readable string representation of the permission and its effect."""
408+
return f"{self.action} - {self.effect}"
409+
410+
def __repr__(self):
411+
"""Developer friendly string representation of the permission."""
412+
return f"{self.action.namespaced_key} => {self.effect}"
413+
380414

381415
@define
382416
class RoleData(AuthZData):
@@ -402,9 +436,17 @@ def name(self) -> str:
402436
"""
403437
return self.external_key.replace("_", " ").title()
404438

439+
def __str__(self):
440+
"""Human readable string representation of the role and its permissions."""
441+
return f"{self.name}: {', '.join(str(p) for p in self.permissions)}"
442+
443+
def __repr__(self):
444+
"""Developer friendly string representation of the role."""
445+
return self.namespaced_key
446+
405447

406448
@define
407-
class RoleAssignmentData(AuthZData):
449+
class RoleAssignmentData:
408450
"""A role assignment is the assignment of a role to a subject in a specific scope.
409451
410452
Attributes:
@@ -416,3 +458,13 @@ class RoleAssignmentData(AuthZData):
416458
subject: SubjectData = None # Needs defaults to avoid value error from attrs
417459
roles: list[RoleData] = []
418460
scope: ScopeData = None
461+
462+
def __str__(self):
463+
"""Human readable string representation of the role assignment."""
464+
role_names = ", ".join(role.name for role in self.roles)
465+
return f"{self.subject} => {role_names} @ {self.scope}"
466+
467+
def __repr__(self):
468+
"""Developer friendly string representation of the role assignment."""
469+
role_keys = ", ".join(role.namespaced_key for role in self.roles)
470+
return f"{self.subject.namespaced_key} => [{role_keys}] @ {self.scope.namespaced_key}"

openedx_authz/api/permissions.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@
55
are not explicitly defined, but are inferred from the policy rules.
66
"""
77

8-
from openedx_authz.api.data import ActionData, PermissionData, PolicyIndex, ScopeData, SubjectData
8+
from openedx_authz.api.data import (
9+
ActionData,
10+
PermissionData,
11+
PolicyIndex,
12+
ScopeData,
13+
SubjectData,
14+
)
915
from openedx_authz.engine.enforcer import enforcer
1016

1117
__all__ = [

openedx_authz/api/roles.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -300,10 +300,12 @@ def get_subject_role_assignments_in_scope(
300300
role_assignments.append(
301301
RoleAssignmentData(
302302
subject=subject,
303-
roles=[RoleData(
304-
namespaced_key=namespaced_key,
305-
permissions=get_permissions_for_single_role(role),
306-
)],
303+
roles=[
304+
RoleData(
305+
namespaced_key=namespaced_key,
306+
permissions=get_permissions_for_single_role(role),
307+
)
308+
],
307309
scope=scope,
308310
)
309311
)
@@ -333,18 +335,22 @@ def get_subject_role_assignments_for_role_in_scope(
333335
role_assignments.append(
334336
RoleAssignmentData(
335337
subject=SubjectData(namespaced_key=subject),
336-
roles=[RoleData(
337-
namespaced_key=role.namespaced_key,
338-
permissions=get_permissions_for_single_role(role),
339-
)],
338+
roles=[
339+
RoleData(
340+
namespaced_key=role.namespaced_key,
341+
permissions=get_permissions_for_single_role(role),
342+
)
343+
],
340344
scope=scope,
341345
)
342346
)
343347

344348
return role_assignments
345349

346350

347-
def get_all_subject_role_assignments_in_scope(scope: ScopeData) -> list[RoleAssignmentData]:
351+
def get_all_subject_role_assignments_in_scope(
352+
scope: ScopeData,
353+
) -> list[RoleAssignmentData]:
348354
"""Get all the subjects assigned to any role in a specific scope.
349355
350356
Args:

openedx_authz/api/users.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@
99
(e.g., 'user@john_doe').
1010
"""
1111

12-
from openedx_authz.api.data import ActionData, RoleAssignmentData, RoleData, ScopeData, UserData
12+
from openedx_authz.api.data import (
13+
ActionData,
14+
RoleAssignmentData,
15+
RoleData,
16+
ScopeData,
17+
UserData,
18+
)
1319
from openedx_authz.api.permissions import is_subject_allowed
1420
from openedx_authz.api.roles import (
1521
assign_role_to_subject_in_scope,
@@ -52,7 +58,9 @@ def assign_role_to_user_in_scope(
5258
)
5359

5460

55-
def batch_assign_role_to_users_in_scope(users: list[str], role_external_key: str, scope_external_key: str):
61+
def batch_assign_role_to_users_in_scope(
62+
users: list[str], role_external_key: str, scope_external_key: str
63+
):
5664
"""Assign a role to multiple users in a specific scope.
5765
5866
Args:
@@ -68,7 +76,9 @@ def batch_assign_role_to_users_in_scope(users: list[str], role_external_key: str
6876
)
6977

7078

71-
def unassign_role_from_user(user_external_key: str, role_external_key: str, scope_external_key: str):
79+
def unassign_role_from_user(
80+
user_external_key: str, role_external_key: str, scope_external_key: str
81+
):
7282
"""Unassign a role from a user in a specific scope.
7383
7484
Args:
@@ -83,7 +93,9 @@ def unassign_role_from_user(user_external_key: str, role_external_key: str, scop
8393
)
8494

8595

86-
def batch_unassign_role_from_users(users: list[str], role_external_key: str, scope_external_key: str):
96+
def batch_unassign_role_from_users(
97+
users: list[str], role_external_key: str, scope_external_key: str
98+
):
8799
"""Unassign a role from multiple users in a specific scope.
88100
89101
Args:

0 commit comments

Comments
 (0)