feat: migrate import linter configuration to pyproject.toml

Migrates complete importlinter configuration from setup.cfg, preserving
all 4 contracts, ignore_imports with explanatory comments, and GitHub
issue references.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Feanil Patel
2026-01-20 11:53:56 -05:00
parent bbc12f1b02
commit b81a81cbd7
2 changed files with 192 additions and 205 deletions

View File

@@ -204,3 +204,195 @@ junit_family = "xunit2"
norecursedirs = ". .* *.egg build conf dist node_modules test_root cms/envs lms/envs openedx/envs"
python_classes = []
python_files = ["tests.py", "test_*.py", "tests_*.py", "*_tests.py", "__init__.py"]
[tool.isort]
indent = " "
line_length = 120
multi_line_output = 3
skip = ["envs", "migrations"]
[tool.importlinter]
root_packages = ["lms", "cms", "openedx", "openedx_content"]
include_external_packages = true
# Our custom contract which checks that we're only importing from 'api.py'
# for participating packages.
contract_types = [
"isolated_apps: openedx.testing.importlinter.isolated_apps_contract.IsolatedAppsContract",
]
[[tool.importlinter.contracts]]
name = "lms and cms are independent"
type = "independence"
modules = ["lms", "cms"]
ignore_imports = [
# lms side imports that we are ignoring for now
"lms.djangoapps.course_home_api.outline.tests.test_view -> cms.djangoapps.contentstore.outlines",
"lms.djangoapps.course_home_api.tests.utils -> cms.djangoapps.contentstore.outlines",
# lms.djangoapps.instructor.tests.test_api & lms.djangoapps.instructor.tests.test_tools
# -> openedx.core.djangoapps.course_date_signals.handlers
# -> cms.djangoapps.contentstore.config.waffle
"openedx.core.djangoapps.course_date_signals.handlers -> cms.djangoapps.contentstore.config.waffle",
# cms side imports that we are ignoring for now
"cms.djangoapps.contentstore.views.tests.test_block -> lms.djangoapps.lms_xblock.mixin",
"cms.djangoapps.contentstore.signals.handlers -> lms.djangoapps.grades.api",
"cms.djangoapps.contentstore.course_group_config -> lms.lib.utils",
"cms.djangoapps.contentstore.views.preview -> lms.djangoapps.lms_xblock.field_data",
# cms.envs.common
# -> openedx.envs.common
# -> lms.djangoapps.lms_xblock.mixin
"openedx.envs.common -> lms.djangoapps.lms_xblock.mixin",
# cms.djangoapps.contentstore.views.tests.test_group_configurations
# -> openedx.features.content_type_gating.helpers
# -> lms.djangoapps.courseware.masquerade
"openedx.features.content_type_gating.helpers -> lms.djangoapps.courseware.masquerade",
# cms.djangoapps.contentstore.utils
# -> openedx.core.djangoapps.django_comment_common.models
# -> openedx.core.djangoapps.course_groups.cohorts
# -> lms.djangoapps.courseware.courses
"openedx.core.djangoapps.course_groups.cohorts -> lms.djangoapps.courseware.courses",
# cms.djangoapps.contentstore.[various]
# -> openedx.features.content_type_gating.partitions
# -> lms.djangoapps.commerce.utils
"openedx.features.content_type_gating.partitions -> lms.djangoapps.commerce.utils",
# cms.djangoapps.contentstore.course_group_config
# -> openedx.core.djangoapps.course_groups.partition_scheme
# -> lms.djangoapps.courseware.masquerade
"openedx.core.djangoapps.course_groups.partition_scheme -> lms.djangoapps.courseware.masquerade",
# cms.djangoapps.export_course_metadata.tasks
# -> openedx.core.djangoapps.schedules.content_highlights
# -> lms.djangoapps.courseware.block_render & lms.djangoapps.courseware.model_data
"openedx.core.djangoapps.content_libraries.* -> lms.djangoapps.*.*",
# cms.djangoapps.contentstore.tasks -> openedx.core.djangoapps.content_libraries.[various]
# -> lms.djangoapps.grades.api
"openedx.core.djangoapps.xblock.*.* -> lms.djangoapps.*.*",
# cms.djangoapps.contentstore.tasks -> openedx.core.djangoapps.content_libraries.[various] -> openedx.core.djangoapps.xblock.[various]
# -> lms.djangoapps.courseware & lms.djangoapps.courseware.grades
"openedx.core.djangoapps.schedules.content_highlights -> lms.djangoapps.courseware.*",
# cms.djangoapps.contentstore.[various]
# -> openedx.core.lib.gating.api
# -> lms.djangoapps.course_blocks.api & lms.djangoapps.courseware.access & lms.djangoapps.grades.api
"openedx.core.lib.gating.api -> lms.djangoapps.*.*",
# cms.djangoapps.contentstore.[various]
# -> openedx.features.content_type_gating.partitions
# -> openedx.features.discounts.utils
# -> lms.djangoapps.courseware.utils & lms.djangoapps.experiments.models
"openedx.features.discounts.utils -> lms.djangoapps.courseware.utils",
"openedx.features.discounts.utils -> lms.djangoapps.experiments.models",
# cms.djangoapps.contentstore.signals.handlers
# -> openedx.core.djangoapps.discussions.tasks
# -> openedx.core.djangoapps.discussions.utils
# -> lms.djangoapps.courseware.access
"openedx.core.djangoapps.discussions.utils -> lms.djangoapps.courseware.access",
# cms.djangoapps.contentstore.[various]
# -> openedx.features.content_type_gating.partitions
# -> openedx.features.discounts.utils
# -> openedx.features.discounts.applicability
# -> lms.djangoapps.courseware.toggles
# & lms.djangoapps.courseware.utils
# & lms.djangoapps.experiments.models
# & lms.djangoapps.experiments.stable_bucketing
"openedx.features.discounts.applicability -> lms.djangoapps.courseware.*",
"openedx.features.discounts.applicability -> lms.djangoapps.experiments.*",
# cms.djangoapps.contentstore.[various]
# -> openedx.core.djangoapps.content.learning_sequences.api
# -> openedx.core.djangoapps.content.learning_sequences.api.outlines
# -> openedx.core.djangoapps.content.learning_sequences.api.permissions
# -> lms.djangoapps.courseware.access
"openedx.core.djangoapps.content.learning_sequences.api.permissions -> lms.djangoapps.courseware.access",
# cms.djangoapps.contentstore.[various]
# -> openedx.features.content_type_gating.partitions
# -> openedx.features.discounts.utils
# -> openedx.features.discounts.applicability
# -> openedx.features.enterprise_support.utils
"openedx.features.enterprise_support.utils -> lms.djangoapps.branding.api",
"cms.djangoapps.contentstore.rest_api.v1.views.settings -> lms.djangoapps.certificates.api",
# We are ignoring this existing import until we can refactor contenstore/helpers.
# https://github.com/openedx/edx-platform/issues/37637
"openedx.core.djangoapps.content_libraries.api.libraries -> cms.djangoapps.contentstore.helpers",
"openedx.core.djangoapps.content_libraries.api.blocks -> cms.djangoapps.contentstore.helpers",
"openedx.core.djangoapps.content_staging.serializers -> cms.djangoapps.contentstore.helpers",
# These imports will become OK once we move content_libraries into CMS
# https://github.com/openedx/edx-platform/issues/33428
"openedx.core.djangoapps.content_libraries.permissions -> cms.djangoapps.course_creators.views",
"openedx.core.djangoapps.content_libraries.tasks -> cms.djangoapps.contentstore.storage",
# Outstanding arch issue: course_overviews is tangled up with LMS
# https://github.com/openedx/edx-platform/issues/37658
"openedx.core.djangoapps.content.course_overviews.models -> lms.djangoapps.**",
# Outstanding arch issue: content_highlights uses courseware block_render
# https://github.com/openedx/edx-platform/issues/37659
"openedx.core.djangoapps.schedules.content_highlights -> lms.djangoapps.courseware.block_render",
]
[[tool.importlinter.contracts]]
name = "Do not depend on non-public API of isolated apps."
type = "isolated_apps"
isolated_apps = [
"cms.djangoapps.modulestore_migrator",
"openedx.core.djangoapps.agreements",
"openedx.core.djangoapps.bookmarks",
"openedx.core.djangoapps.content_libraries",
"openedx.core.djangoapps.content_staging",
"openedx.core.djangoapps.olx_rest_api",
"openedx.core.djangoapps.xblock",
"openedx.core.lib.xblock_serializer",
]
allowed_modules = [
# Only imports from api.py and data.py are allowed elsewhere in the code
# See https://open-edx-proposals.readthedocs.io/en/latest/best-practices/oep-0049-django-app-patterns.html#api-py
"api",
"data",
"tests",
]
[[tool.importlinter.contracts]]
name = "Do not directly import internals of openedx_content (only import from openedx_content.api)."
type = "forbidden"
source_modules = ["cms", "lms", "openedx", "common", "xmodule"]
forbidden_modules = ["openedx_content.applets", "openedx_content.backcompat"]
allow_indirect_imports = true
[[tool.importlinter.contracts]]
name = "Low-level apps should not depend on high-level apps"
type = "layers"
layers = [
# Layers from high-level to low-level. Imports should only occur from higher to lower.
"cms.lib.xblock.upstream_sync | openedx.core.djangoapps.content.search | openedx.core.djangoapps.olx_rest_api",
"openedx.core.djangoapps.content_libraries",
"openedx.core.djangoapps.content_staging",
"openedx.core.djangoapps.xblock",
"openedx.core.lib.xblock_serializer",
"openedx.core.djangoapps.content_tagging",
]
ignore_imports = [
# Test code can break these layering rules
"**.tests.** -> **",
# FIXME: the exceptions below are from before we added this import linting rule. Should refactor to eliminate them.
# In particular, the contentstore.helpers module is too big and has too many imports
# See https://github.com/openedx/edx-platform/issues/37637
# The CSV export hard-codes support for courses and libraries. Refactor to do something like learning_context.get_children()
"openedx.core.djangoapps.content_tagging.helpers.objecttag_export_helpers -> openedx.core.djangoapps.content_libraries.api",
# The permissions checking code for tagging requires libraries model data to get all the orgs that a user is using:
"openedx.core.djangoapps.content_tagging.utils -> openedx.core.djangoapps.content_libraries.api",
# Content staging POST to clipboard API uses libraries APIs. We're working on moving this code to content_libraries
"openedx.core.djangoapps.content_staging.views -> openedx.core.djangoapps.content_libraries.api",
# content_staging.serializers imports contentstore.helpers which imports contentstore.utils which imports the libraries API.
"openedx.core.djangoapps.content_staging.serializers -> cms.djangoapps.contentstore.helpers",
# content_libraries.rest_api.libraries imports cms.djangoapps.contentstore.views.course which imports
# contentstore.toggles which imports djangoapps.content.search.api
"openedx.core.djangoapps.content_libraries.rest_api.libraries -> cms.djangoapps.contentstore.views.course",
# Content libraries imports contentstore.helpers which imports upstream_sync
"openedx.core.djangoapps.content_libraries.api.blocks -> cms.djangoapps.contentstore.helpers",
"openedx.core.djangoapps.content_libraries.api.libraries -> cms.djangoapps.contentstore.helpers",
# Outstanding arch issue: course_overviews is tangled up with LMS
# https://github.com/openedx/edx-platform/issues/37658
"openedx.core.djangoapps.content.course_overviews.models -> lms.djangoapps.**",
# Outstanding arch issue: content_highlights uses courseware block_render
# https://github.com/openedx/edx-platform/issues/37659
"openedx.core.djangoapps.schedules.content_highlights -> lms.djangoapps.courseware.block_render",
# This import happens only because grading signals are defined in LMS rather than openedx-events
# https://github.com/openedx/edx-platform/issues/37660
"openedx.core.djangoapps.xblock.runtime.runtime -> lms.djangoapps.grades.api",
# These imports will become OK once we worked on the following issue:
# https://github.com/openedx/edx-platform/issues/33428
"openedx.core.djangoapps.video_config.services -> openedx.core.djangoapps.content_libraries.api",
]

205
setup.cfg
View File

@@ -20,208 +20,3 @@
# 1.5.7 that we haven't cleaned up yet
ignore=E203,E265,E266,E305,E402,E501,E722,E731,E741,E743,W503,W504,W602
exclude=migrations,.git,.pycharm_helpers,.tox,test_root/staticfiles,node_modules
[isort]
indent=' '
line_length=120
multi_line_output=3
skip=
envs
migrations
[importlinter]
root_packages =
lms
cms
openedx
openedx_content
include_external_packages = True
contract_types =
# Our custom contract which checks that we're only importing from 'api.py'
# for participating packages.
isolated_apps: openedx.testing.importlinter.isolated_apps_contract.IsolatedAppsContract
[importlinter:contract:1]
name = lms and cms are independent
type = independence
modules =
lms
cms
ignore_imports =
############################################################################
# lms side imports that we are ignoring for now
lms.djangoapps.course_home_api.outline.tests.test_view -> cms.djangoapps.contentstore.outlines
lms.djangoapps.course_home_api.tests.utils -> cms.djangoapps.contentstore.outlines
# lms.djangoapps.instructor.tests.test_api & lms.djangoapps.instructor.tests.test_tools
# -> openedx.core.djangoapps.course_date_signals.handlers
# -> cms.djangoapps.contentstore.config.waffle
openedx.core.djangoapps.course_date_signals.handlers -> cms.djangoapps.contentstore.config.waffle
############################################################################
# cms side imports that we are ignoring for now
cms.djangoapps.contentstore.views.tests.test_block -> lms.djangoapps.lms_xblock.mixin
cms.djangoapps.contentstore.signals.handlers -> lms.djangoapps.grades.api
cms.djangoapps.contentstore.course_group_config -> lms.lib.utils
cms.djangoapps.contentstore.views.preview -> lms.djangoapps.lms_xblock.field_data
# cms.envs.common
# -> openedx.envs.common
# -> lms.djangoapps.lms_xblock.mixin
openedx.envs.common -> lms.djangoapps.lms_xblock.mixin
# cms.djangoapps.contentstore.views.tests.test_group_configurations
# -> openedx.features.content_type_gating.helpers
# -> lms.djangoapps.courseware.masquerade
openedx.features.content_type_gating.helpers -> lms.djangoapps.courseware.masquerade
# cms.djangoapps.contentstore.utils
# -> openedx.core.djangoapps.django_comment_common.models
# -> openedx.core.djangoapps.course_groups.cohorts
# -> lms.djangoapps.courseware.courses
openedx.core.djangoapps.course_groups.cohorts -> lms.djangoapps.courseware.courses
# cms.djangoapps.contentstore.[various]
# -> openedx.features.content_type_gating.partitions
# -> lms.djangoapps.commerce.utils
openedx.features.content_type_gating.partitions -> lms.djangoapps.commerce.utils
# cms.djangoapps.contentstore.course_group_config
# -> openedx.core.djangoapps.course_groups.partition_scheme
# -> lms.djangoapps.courseware.masquerade
openedx.core.djangoapps.course_groups.partition_scheme -> lms.djangoapps.courseware.masquerade
# cms.djangoapps.export_course_metadata.tasks
# -> openedx.core.djangoapps.schedules.content_highlights
# -> lms.djangoapps.courseware.block_render & lms.djangoapps.courseware.model_data
openedx.core.djangoapps.content_libraries.* -> lms.djangoapps.*.*
# cms.djangoapps.contentstore.tasks -> openedx.core.djangoapps.content_libraries.[various]
# -> lms.djangoapps.grades.api
openedx.core.djangoapps.xblock.*.* -> lms.djangoapps.*.*
# cms.djangoapps.contentstore.tasks -> openedx.core.djangoapps.content_libraries.[various] -> openedx.core.djangoapps.xblock.[various]
# -> lms.djangoapps.courseware & lms.djangoapps.courseware.grades
openedx.core.djangoapps.schedules.content_highlights -> lms.djangoapps.courseware.*
# cms.djangoapps.contentstore.[various]
# -> openedx.core.lib.gating.api
# -> lms.djangoapps.course_blocks.api & lms.djangoapps.courseware.access & lms.djangoapps.grades.api
openedx.core.lib.gating.api -> lms.djangoapps.*.*
# cms.djangoapps.contentstore.[various]
# -> openedx.features.content_type_gating.partitions
# -> openedx.features.discounts.utils
# -> lms.djangoapps.courseware.utils & lms.djangoapps.experiments.models
openedx.features.discounts.utils -> lms.djangoapps.courseware.utils
openedx.features.discounts.utils -> lms.djangoapps.experiments.models
# cms.djangoapps.contentstore.signals.handlers
# -> openedx.core.djangoapps.discussions.tasks
# -> openedx.core.djangoapps.discussions.utils
# -> lms.djangoapps.courseware.access
openedx.core.djangoapps.discussions.utils -> lms.djangoapps.courseware.access
# cms.djangoapps.contentstore.[various]
# -> openedx.features.content_type_gating.partitions
# -> openedx.features.discounts.utils
# -> openedx.features.discounts.applicability
# -> lms.djangoapps.courseware.toggles
# & lms.djangoapps.courseware.utils
# & lms.djangoapps.experiments.models
# & lms.djangoapps.experiments.stable_bucketing
openedx.features.discounts.applicability -> lms.djangoapps.courseware.*
openedx.features.discounts.applicability -> lms.djangoapps.experiments.*
# cms.djangoapps.contentstore.[various]
# -> openedx.core.djangoapps.content.learning_sequences.api
# -> openedx.core.djangoapps.content.learning_sequences.api.outlines
# -> openedx.core.djangoapps.content.learning_sequences.api.permissions
# -> lms.djangoapps.courseware.access
openedx.core.djangoapps.content.learning_sequences.api.permissions -> lms.djangoapps.courseware.access
# cms.djangoapps.contentstore.[various]
# -> openedx.features.content_type_gating.partitions
# -> openedx.features.discounts.utils
# -> openedx.features.discounts.applicability
# -> openedx.features.enterprise_support.utils
openedx.features.enterprise_support.utils -> lms.djangoapps.branding.api
cms.djangoapps.contentstore.rest_api.v1.views.settings -> lms.djangoapps.certificates.api
# We are ignoring this existing import until we can refactor contenstore/helpers.
# https://github.com/openedx/edx-platform/issues/37637
openedx.core.djangoapps.content_libraries.api.libraries -> cms.djangoapps.contentstore.helpers
openedx.core.djangoapps.content_libraries.api.blocks -> cms.djangoapps.contentstore.helpers
openedx.core.djangoapps.content_staging.serializers -> cms.djangoapps.contentstore.helpers
# These imports will become OK once we move content_libraries into CMS
# https://github.com/openedx/edx-platform/issues/33428
openedx.core.djangoapps.content_libraries.permissions -> cms.djangoapps.course_creators.views
openedx.core.djangoapps.content_libraries.tasks -> cms.djangoapps.contentstore.storage
# Outstanding arch issue: course_overviews is tangled up with LMS
# https://github.com/openedx/edx-platform/issues/37658
openedx.core.djangoapps.content.course_overviews.models -> lms.djangoapps.**
# Outstanding arch issue: content_highlights uses courseware block_render
# https://github.com/openedx/edx-platform/issues/37659
openedx.core.djangoapps.schedules.content_highlights -> lms.djangoapps.courseware.block_render
[importlinter:contract:2]
name = Do not depend on non-public API of isolated apps.
type = isolated_apps
isolated_apps =
cms.djangoapps.modulestore_migrator
openedx.core.djangoapps.agreements
openedx.core.djangoapps.bookmarks
openedx.core.djangoapps.content_libraries
openedx.core.djangoapps.content_staging
openedx.core.djangoapps.olx_rest_api
openedx.core.djangoapps.xblock
openedx.core.lib.xblock_serializer
allowed_modules =
# Only imports from api.py and data.py are allowed elsewhere in the code
# See https://open-edx-proposals.readthedocs.io/en/latest/best-practices/oep-0049-django-app-patterns.html#api-py
api
data
tests
[importlinter:contract:3]
name = Do not directly import internals of openedx_content (only import from openedx_content.api).
type = forbidden
source_modules =
cms
lms
openedx
common
xmodule
forbidden_modules =
openedx_content.applets
openedx_content.backcompat
allow_indirect_imports = True
[importlinter:contract:4]
name = Low-level apps should not depend on high-level apps
type = layers
layers =
# Layers from high-level to low-level. Imports should only occur from higher to lower.
cms.lib.xblock.upstream_sync | openedx.core.djangoapps.content.search | openedx.core.djangoapps.olx_rest_api
openedx.core.djangoapps.content_libraries
openedx.core.djangoapps.content_staging
openedx.core.djangoapps.xblock
openedx.core.lib.xblock_serializer
openedx.core.djangoapps.content_tagging
ignore_imports =
# Test code can break these layering rules
**.tests.** -> **
# FIXME: the exceptions below are from before we added this import linting rule. Should refactor to eliminate them.
# In particular, the contentstore.helpers module is too big and has too many imports
# See https://github.com/openedx/edx-platform/issues/37637
# The CSV export hard-codes support for courses and libraries. Refactor to do something like learning_context.get_children()
openedx.core.djangoapps.content_tagging.helpers.objecttag_export_helpers -> openedx.core.djangoapps.content_libraries.api
# The permissions checking code for tagging requires libraries model data to get all the orgs that a user is using:
openedx.core.djangoapps.content_tagging.utils -> openedx.core.djangoapps.content_libraries.api
# Content staging POST to clipboard API uses libraries APIs. We're working on moving this code to content_libraries
openedx.core.djangoapps.content_staging.views -> openedx.core.djangoapps.content_libraries.api
# content_staging.serializers imports contentstore.helpers which imports contentstore.utils which imports the libraries API.
openedx.core.djangoapps.content_staging.serializers -> cms.djangoapps.contentstore.helpers
# content_libraries.rest_api.libraries imports cms.djangoapps.contentstore.views.course which imports
# contentstore.toggles which imports djangoapps.content.search.api
openedx.core.djangoapps.content_libraries.rest_api.libraries -> cms.djangoapps.contentstore.views.course
# Content libraries imports contentstore.helpers which imports upstream_sync
openedx.core.djangoapps.content_libraries.api.blocks -> cms.djangoapps.contentstore.helpers
openedx.core.djangoapps.content_libraries.api.libraries -> cms.djangoapps.contentstore.helpers
# Outstanding arch issue: course_overviews is tangled up with LMS
# https://github.com/openedx/edx-platform/issues/37658
openedx.core.djangoapps.content.course_overviews.models -> lms.djangoapps.**
# Outstanding arch issue: content_highlights uses courseware block_render
# https://github.com/openedx/edx-platform/issues/37659
openedx.core.djangoapps.schedules.content_highlights -> lms.djangoapps.courseware.block_render
# This import happens only because grading signals are defined in LMS rather than openedx-events
# https://github.com/openedx/edx-platform/issues/37660
openedx.core.djangoapps.xblock.runtime.runtime -> lms.djangoapps.grades.api
# These imports will become OK once we worked on the following issue:
# https://github.com/openedx/edx-platform/issues/33428
openedx.core.djangoapps.video_config.services -> openedx.core.djangoapps.content_libraries.api