Skip to content

Commit 04d8a9f

Browse files
[FC-0099] chore: migrate to ruff instead of pycodestyle and isort (openedx#108)
1 parent ed31980 commit 04d8a9f

29 files changed

Lines changed: 491 additions & 349 deletions

CHANGELOG.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Change Log
1414
Unreleased
1515
**********
1616

17-
*
17+
* Migrate from using pycodestyle and isort to ruff for code quality checks and formatting.
1818

1919
0.6.0 - 2025-10-22
2020
******************

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ upgrade: ## update the requirements/*.txt files with the latest packages satisfy
5858
quality: ## check coding style with pycodestyle and pylint
5959
tox -e quality
6060

61+
format: ## format code with black and isort. Enable ruff to fix E (pycodestyle) and I (isort) issues
62+
ruff format openedx_authz tests test_utils manage.py setup.py
63+
ruff check --fix openedx_authz tests test_utils manage.py setup.py
64+
6165
pii_check: ## check for PII annotations on all Django models
6266
tox -e pii_check
6367

openedx_authz/api/data.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,7 @@ def get_subclass_by_external_key(mcs, external_key: str) -> Type["ScopeData"]:
248248
scope_subclass = mcs.scope_registry.get(namespace)
249249

250250
if not scope_subclass:
251-
raise ValueError(
252-
f"Unknown scope: {namespace} for external_key: {external_key}"
253-
)
251+
raise ValueError(f"Unknown scope: {namespace} for external_key: {external_key}")
254252

255253
if not scope_subclass.validate_external_key(external_key):
256254
raise ValueError(f"Invalid external_key format: {external_key}")
@@ -281,9 +279,7 @@ def validate_external_key(mcs, external_key: str) -> bool:
281279
Returns:
282280
bool: True if valid, False otherwise.
283281
"""
284-
raise NotImplementedError(
285-
"Subclasses must implement validate_external_key method."
286-
)
282+
raise NotImplementedError("Subclasses must implement validate_external_key method.")
287283

288284

289285
@define

openedx_authz/api/permissions.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@ def get_all_permissions_in_scope(scope: ScopeData) -> list[PermissionData]:
4343
list of PermissionData: A list of PermissionData objects associated with the given scope.
4444
"""
4545
enforcer = AuthzEnforcer.get_enforcer()
46-
actions = enforcer.get_filtered_policy(
47-
PolicyIndex.SCOPE.value, scope.namespaced_key
48-
)
46+
actions = enforcer.get_filtered_policy(PolicyIndex.SCOPE.value, scope.namespaced_key)
4947
return [get_permission_from_policy(action) for action in actions]
5048

5149

@@ -65,6 +63,4 @@ def is_subject_allowed(
6563
bool: True if the subject has the specified permission in the scope, False otherwise.
6664
"""
6765
enforcer = AuthzEnforcer.get_enforcer()
68-
return enforcer.enforce(
69-
subject.namespaced_key, action.namespaced_key, scope.namespaced_key
70-
)
66+
return enforcer.enforce(subject.namespaced_key, action.namespaced_key, scope.namespaced_key)

openedx_authz/api/roles.py

Lines changed: 17 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,7 @@ def get_permissions_for_roles(
7878
permissions_by_role = {}
7979

8080
for role in roles:
81-
permissions_by_role[role.external_key] = {
82-
"permissions": get_permissions_for_single_role(role)
83-
}
81+
permissions_by_role[role.external_key] = {"permissions": get_permissions_for_single_role(role)}
8482

8583
return permissions_by_role
8684

@@ -116,22 +114,15 @@ def get_permissions_for_active_roles_in_scope(
116114
permissions and scopes.
117115
"""
118116
enforcer = AuthzEnforcer.get_enforcer()
119-
filtered_policy = enforcer.get_filtered_grouping_policy(
120-
GroupingPolicyIndex.SCOPE.value, scope.namespaced_key
121-
)
117+
filtered_policy = enforcer.get_filtered_grouping_policy(GroupingPolicyIndex.SCOPE.value, scope.namespaced_key)
122118

123119
if role:
124120
filtered_policy = [
125-
policy
126-
for policy in filtered_policy
127-
if policy[GroupingPolicyIndex.ROLE.value] == role.namespaced_key
121+
policy for policy in filtered_policy if policy[GroupingPolicyIndex.ROLE.value] == role.namespaced_key
128122
]
129123

130124
return get_permissions_for_roles(
131-
[
132-
RoleData(namespaced_key=policy[GroupingPolicyIndex.ROLE.value])
133-
for policy in filtered_policy
134-
]
125+
[RoleData(namespaced_key=policy[GroupingPolicyIndex.ROLE.value]) for policy in filtered_policy]
135126
)
136127

137128

@@ -148,9 +139,7 @@ def get_role_definitions_in_scope(scope: ScopeData) -> list[RoleData]:
148139
list[Role]: A list of roles.
149140
"""
150141
enforcer = AuthzEnforcer.get_enforcer()
151-
policy_filtered = enforcer.get_filtered_policy(
152-
PolicyIndex.SCOPE.value, scope.namespaced_key
153-
)
142+
policy_filtered = enforcer.get_filtered_policy(PolicyIndex.SCOPE.value, scope.namespaced_key)
154143

155144
permissions_per_role = defaultdict(
156145
lambda: {
@@ -162,9 +151,7 @@ def get_role_definitions_in_scope(scope: ScopeData) -> list[RoleData]:
162151
permissions_per_role[policy[PolicyIndex.ROLE.value]]["scopes"].append(
163152
ScopeData(namespaced_key=policy[PolicyIndex.SCOPE.value])
164153
) # TODO: I don't think this actually gets used anywhere
165-
permissions_per_role[policy[PolicyIndex.ROLE.value]]["permissions"].append(
166-
get_permission_from_policy(policy)
167-
)
154+
permissions_per_role[policy[PolicyIndex.ROLE.value]]["permissions"].append(get_permission_from_policy(policy))
168155

169156
return [
170157
RoleData(
@@ -194,14 +181,10 @@ def get_all_roles_in_scope(scope: ScopeData) -> list[list[str]]:
194181
list[list[str]]: A list of policies in the specified scope.
195182
"""
196183
enforcer = AuthzEnforcer.get_enforcer()
197-
return enforcer.get_filtered_grouping_policy(
198-
GroupingPolicyIndex.SCOPE.value, scope.namespaced_key
199-
)
184+
return enforcer.get_filtered_grouping_policy(GroupingPolicyIndex.SCOPE.value, scope.namespaced_key)
200185

201186

202-
def assign_role_to_subject_in_scope(
203-
subject: SubjectData, role: RoleData, scope: ScopeData
204-
) -> bool:
187+
def assign_role_to_subject_in_scope(subject: SubjectData, role: RoleData, scope: ScopeData) -> bool:
205188
"""Assign a role to a subject.
206189
207190
Args:
@@ -220,9 +203,7 @@ def assign_role_to_subject_in_scope(
220203
)
221204

222205

223-
def batch_assign_role_to_subjects_in_scope(
224-
subjects: list[SubjectData], role: RoleData, scope: ScopeData
225-
) -> None:
206+
def batch_assign_role_to_subjects_in_scope(subjects: list[SubjectData], role: RoleData, scope: ScopeData) -> None:
226207
"""Assign a role to a list of subjects.
227208
228209
Args:
@@ -233,9 +214,7 @@ def batch_assign_role_to_subjects_in_scope(
233214
assign_role_to_subject_in_scope(subject, role, scope)
234215

235216

236-
def unassign_role_from_subject_in_scope(
237-
subject: SubjectData, role: RoleData, scope: ScopeData
238-
) -> bool:
217+
def unassign_role_from_subject_in_scope(subject: SubjectData, role: RoleData, scope: ScopeData) -> bool:
239218
"""Unassign a role from a subject.
240219
241220
Args:
@@ -247,14 +226,10 @@ def unassign_role_from_subject_in_scope(
247226
bool: True if the role was unassigned successfully, False otherwise.
248227
"""
249228
enforcer = AuthzEnforcer.get_enforcer()
250-
return enforcer.delete_roles_for_user_in_domain(
251-
subject.namespaced_key, role.namespaced_key, scope.namespaced_key
252-
)
229+
return enforcer.delete_roles_for_user_in_domain(subject.namespaced_key, role.namespaced_key, scope.namespaced_key)
253230

254231

255-
def batch_unassign_role_from_subjects_in_scope(
256-
subjects: list[SubjectData], role: RoleData, scope: ScopeData
257-
) -> None:
232+
def batch_unassign_role_from_subjects_in_scope(subjects: list[SubjectData], role: RoleData, scope: ScopeData) -> None:
258233
"""Unassign a role from a list of subjects.
259234
260235
Args:
@@ -277,9 +252,7 @@ def get_subject_role_assignments(subject: SubjectData) -> list[RoleAssignmentDat
277252
"""
278253
enforcer = AuthzEnforcer.get_enforcer()
279254
role_assignments = []
280-
for policy in enforcer.get_filtered_grouping_policy(
281-
GroupingPolicyIndex.SUBJECT.value, subject.namespaced_key
282-
):
255+
for policy in enforcer.get_filtered_grouping_policy(GroupingPolicyIndex.SUBJECT.value, subject.namespaced_key):
283256
role = RoleData(namespaced_key=policy[GroupingPolicyIndex.ROLE.value])
284257
role.permissions = get_permissions_for_single_role(role)
285258

@@ -293,9 +266,7 @@ def get_subject_role_assignments(subject: SubjectData) -> list[RoleAssignmentDat
293266
return role_assignments
294267

295268

296-
def get_subject_role_assignments_in_scope(
297-
subject: SubjectData, scope: ScopeData
298-
) -> list[RoleAssignmentData]:
269+
def get_subject_role_assignments_in_scope(subject: SubjectData, scope: ScopeData) -> list[RoleAssignmentData]:
299270
"""Get the roles for a subject in a specific scope.
300271
301272
Args:
@@ -308,9 +279,7 @@ def get_subject_role_assignments_in_scope(
308279
enforcer = AuthzEnforcer.get_enforcer()
309280
# TODO: we still need to get the remaining data for the role like email, etc
310281
role_assignments = []
311-
for namespaced_key in enforcer.get_roles_for_user_in_domain(
312-
subject.namespaced_key, scope.namespaced_key
313-
):
282+
for namespaced_key in enforcer.get_roles_for_user_in_domain(subject.namespaced_key, scope.namespaced_key):
314283
role = RoleData(namespaced_key=namespaced_key)
315284
role_assignments.append(
316285
RoleAssignmentData(
@@ -327,9 +296,7 @@ def get_subject_role_assignments_in_scope(
327296
return role_assignments
328297

329298

330-
def get_subject_role_assignments_for_role_in_scope(
331-
role: RoleData, scope: ScopeData
332-
) -> list[RoleAssignmentData]:
299+
def get_subject_role_assignments_for_role_in_scope(role: RoleData, scope: ScopeData) -> list[RoleAssignmentData]:
333300
"""Get the subjects assigned to a specific role in a specific scope.
334301
335302
Args:
@@ -341,9 +308,7 @@ def get_subject_role_assignments_for_role_in_scope(
341308
"""
342309
enforcer = AuthzEnforcer.get_enforcer()
343310
role_assignments = []
344-
for subject in enforcer.get_users_for_role_in_domain(
345-
role.namespaced_key, scope.namespaced_key
346-
):
311+
for subject in enforcer.get_users_for_role_in_domain(role.namespaced_key, scope.namespaced_key):
347312
if subject.startswith(f"{RoleData.NAMESPACE}{RoleData.SEPARATOR}"):
348313
# Skip roles that are also subjects
349314
continue

openedx_authz/engine/adapter.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,11 @@ def is_filtered(self) -> bool:
7676
"""
7777
return True
7878

79-
def load_filtered_policy(self, model: Model, filter: Filter) -> None: # pylint: disable=redefined-builtin
79+
def load_filtered_policy(
80+
self,
81+
model: Model,
82+
filter: Filter, # pylint: disable=redefined-builtin
83+
) -> None:
8084
"""
8185
Load policy rules from storage with filtering applied.
8286
@@ -99,7 +103,11 @@ def load_filtered_policy(self, model: Model, filter: Filter) -> None: # pylint:
99103
for line in filtered_queryset:
100104
persist.load_policy_line(str(line), model)
101105

102-
def filter_query(self, queryset: QuerySet, filter: Filter) -> QuerySet: # pylint: disable=redefined-builtin
106+
def filter_query(
107+
self,
108+
queryset: QuerySet,
109+
filter: Filter, # pylint: disable=redefined-builtin
110+
) -> QuerySet:
103111
"""
104112
Apply filter criteria to the policy queryset.
105113

openedx_authz/engine/utils.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,31 +51,21 @@ def migrate_policy_between_enforcers(
5151

5252
for grouping_policy_ptype in GROUPING_POLICY_PTYPES:
5353
try:
54-
grouping_policies = source_enforcer.get_named_grouping_policy(
55-
grouping_policy_ptype
56-
)
54+
grouping_policies = source_enforcer.get_named_grouping_policy(grouping_policy_ptype)
5755
for grouping in grouping_policies:
58-
if target_enforcer.has_named_grouping_policy(
59-
grouping_policy_ptype, *grouping
60-
):
56+
if target_enforcer.has_named_grouping_policy(grouping_policy_ptype, *grouping):
6157
logger.info(
6258
f"Grouping policy {grouping_policy_ptype}, {grouping} already exists in target, skipping."
6359
)
6460
continue
65-
target_enforcer.add_named_grouping_policy(
66-
grouping_policy_ptype, *grouping
67-
)
61+
target_enforcer.add_named_grouping_policy(grouping_policy_ptype, *grouping)
6862

6963
# Ensure latest policies are loaded in the target enforcer after each addition
7064
# to avoid duplicates
7165
target_enforcer.load_policy()
7266
except KeyError as e:
73-
logger.info(
74-
f"Skipping {grouping_policy_ptype} policies: {e} not found in source enforcer."
75-
)
76-
logger.info(
77-
f"Successfully loaded policies from {source_enforcer.get_model()} into the database."
78-
)
67+
logger.info(f"Skipping {grouping_policy_ptype} policies: {e} not found in source enforcer.")
68+
logger.info(f"Successfully loaded policies from {source_enforcer.get_model()} into the database.")
7969
except Exception as e:
8070
logger.error(f"Error loading policies from file: {e}")
8171
raise

openedx_authz/management/commands/enforcement.py

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,7 @@ def handle(self, *args, **options):
7878
Raises:
7979
CommandError: If model or policy files are not found or enforcer creation fails.
8080
"""
81-
model_file_path = (
82-
self._get_file_path("model.conf") or options["model_file_path"]
83-
)
81+
model_file_path = self._get_file_path("model.conf") or options["model_file_path"]
8482
policy_file_path = options["policy_file_path"]
8583

8684
if not os.path.isfile(model_file_path):
@@ -95,9 +93,7 @@ def handle(self, *args, **options):
9593

9694
try:
9795
enforcer = casbin.Enforcer(model_file_path, policy_file_path)
98-
self.stdout.write(
99-
self.style.SUCCESS("Casbin enforcer created successfully")
100-
)
96+
self.stdout.write(self.style.SUCCESS("Casbin enforcer created successfully"))
10197

10298
policies = enforcer.get_policy()
10399
roles = enforcer.get_grouping_policy()
@@ -160,9 +156,7 @@ def _run_interactive_mode(self, enforcer: casbin.Enforcer) -> None:
160156
self.stdout.write(self.style.ERROR("Exiting interactive mode..."))
161157
break
162158

163-
def _test_interactive_request(
164-
self, enforcer: casbin.Enforcer, user_input: str
165-
) -> None:
159+
def _test_interactive_request(self, enforcer: casbin.Enforcer, user_input: str) -> None:
166160
"""Process and test a single enforcement request from user input.
167161
168162
Parses the input string, validates the format, executes the enforcement
@@ -180,11 +174,7 @@ def _test_interactive_request(
180174
try:
181175
parts = [part.strip() for part in user_input.split()]
182176
if len(parts) != 3:
183-
self.stdout.write(
184-
self.style.ERROR(
185-
f"✗ Invalid format. Expected 3 parts, got {len(parts)}"
186-
)
187-
)
177+
self.stdout.write(self.style.ERROR(f"✗ Invalid format. Expected 3 parts, got {len(parts)}"))
188178
self.stdout.write("Format: subject action scope")
189179
self.stdout.write("Example: user^alice act^read org^OpenedX")
190180
return
@@ -193,13 +183,9 @@ def _test_interactive_request(
193183
result = enforcer.enforce(subject, action, scope)
194184

195185
if result:
196-
self.stdout.write(
197-
self.style.SUCCESS(f"✓ ALLOWED: {subject} {action} {scope}")
198-
)
186+
self.stdout.write(self.style.SUCCESS(f"✓ ALLOWED: {subject} {action} {scope}"))
199187
else:
200-
self.stdout.write(
201-
self.style.ERROR(f"✗ DENIED: {subject} {action} {scope}")
202-
)
188+
self.stdout.write(self.style.ERROR(f"✗ DENIED: {subject} {action} {scope}"))
203189

204190
except (ValueError, IndexError, TypeError) as e:
205191
self.stdout.write(self.style.ERROR(f"✗ Error processing request: {str(e)}"))

0 commit comments

Comments
 (0)