From 0d2bde4d5dd5daa2fbe41247808102e82590a422 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 11:07:35 -0500 Subject: [PATCH 01/15] feat: initialize pyproject.toml with build system Adds initial pyproject.toml with build system configuration using setuptools>=61.0 (PEP 621 compatible). No functional changes yet - setup.py remains fully functional. Co-Authored-By: Claude Opus 4.5 --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000000..638dd9c54fcb --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" From 24685d06bea12eadbe14238bb83241054c2ba263 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 11:13:07 -0500 Subject: [PATCH 02/15] feat: migrate package metadata to pyproject.toml Migrates package metadata from setup.py including version, dependencies, and package definitions. Changes package name from "Open edX" to "openedx-platform" to comply with PEP 508 (no spaces allowed). This is safe as the package is not published and dependents rely on entry points, not the package name. Simplifies setup.py to contain only entry_points, which will be migrated in subsequent commits. Co-Authored-By: Claude Opus 4.5 --- pyproject.toml | 12 ++++++++++++ setup.py | 16 ---------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 638dd9c54fcb..e8e86e28c49f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,15 @@ [build-system] requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" + +[project] +name = "openedx-platform" +version = "0.13" +requires-python = ">=3.11" +dependencies = ["setuptools"] + +[tool.setuptools] +packages = ["cms", "common", "lms", "openedx", "xmodule"] + +[tool.setuptools.package-data] +xmodule = ["js/module/*"] diff --git a/setup.py b/setup.py index eeb7b79f534d..20ce86b16e0b 100644 --- a/setup.py +++ b/setup.py @@ -45,22 +45,6 @@ setup( - name="Open edX", - version='0.13', - install_requires=["setuptools"], - requires=[], - # NOTE: These are not the names we should be installing. This tree should - # be reorganized to be a more conventional Python tree. - packages=[ - "cms", - "common", - "lms", - "openedx", - "xmodule", - ], - package_data={ - 'xmodule': ['js/module/*'], - }, entry_points={ "openedx.course_tab": [ "ccx = lms.djangoapps.ccx.plugins:CcxCourseTab", From 474fec7c9d3bce45a74a7a7db4b3d7f2c8d6df95 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 11:26:06 -0500 Subject: [PATCH 03/15] feat: migrate XBlock entry points to pyproject.toml Migrates 30 xblock.v1 entry points from setup.py. Removes XBLOCKS constant as it's no longer needed. Co-Authored-By: Claude Opus 4.5 --- pyproject.toml | 35 +++++++++++++++++++++++++++++++++++ setup.py | 36 ------------------------------------ 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e8e86e28c49f..0b72676081dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,3 +13,38 @@ packages = ["cms", "common", "lms", "openedx", "xmodule"] [tool.setuptools.package-data] xmodule = ["js/module/*"] + +[project.entry-points."xblock.v1"] +about = "xmodule.html_block:AboutBlock" +book = "xmodule.template_block:TranslateCustomTagBlock" +annotatable = "xmodule.annotatable_block:AnnotatableBlock" +chapter = "xmodule.seq_block:SectionBlock" +conditional = "xmodule.conditional_block:ConditionalBlock" +course = "xmodule.course_block:CourseBlock" +course_info = "xmodule.html_block:CourseInfoBlock" +customtag = "xmodule.template_block:CustomTagBlock" +custom_tag_template = "xmodule.template_block:CustomTagTemplateBlock" +discuss = "xmodule.template_block:TranslateCustomTagBlock" +discussion = "xmodule.discussion_block:DiscussionXBlock" +error = "xmodule.error_block:ErrorBlock" +hidden = "xmodule.hidden_block:HiddenBlock" +html = "xmodule.html_block:HtmlBlock" +itembank = "xmodule.item_bank_block:ItemBankBlock" +image = "xmodule.template_block:TranslateCustomTagBlock" +library = "xmodule.library_root_xblock:LibraryRoot" +library_content = "xmodule.library_content_block:LegacyLibraryContentBlock" +lti = "xmodule.lti_block:LTIBlock" +poll_question = "xmodule.poll_block:PollBlock" +problem = "xmodule.capa_block:ProblemBlock" +randomize = "xmodule.randomize_block:RandomizeBlock" +sequential = "xmodule.seq_block:SequenceBlock" +slides = "xmodule.template_block:TranslateCustomTagBlock" +split_test = "xmodule.split_test_block:SplitTestBlock" +static_tab = "xmodule.html_block:StaticTabBlock" +unit = "xmodule.unit_block:UnitBlock" +vertical = "xmodule.vertical_block:VerticalBlock" +video = "xmodule.video_block:VideoBlock" +videoalpha = "xmodule.video_block:VideoBlock" +videodev = "xmodule.template_block:TranslateCustomTagBlock" +word_cloud = "xmodule.word_cloud_block:WordCloudBlock" +wrapper = "xmodule.wrapper_block:WrapperBlock" diff --git a/setup.py b/setup.py index 20ce86b16e0b..dd92884efb9b 100644 --- a/setup.py +++ b/setup.py @@ -4,41 +4,6 @@ from setuptools import setup -XBLOCKS = [ - "about = xmodule.html_block:AboutBlock", - "book = xmodule.template_block:TranslateCustomTagBlock", - "annotatable = xmodule.annotatable_block:AnnotatableBlock", - "chapter = xmodule.seq_block:SectionBlock", - "conditional = xmodule.conditional_block:ConditionalBlock", - "course = xmodule.course_block:CourseBlock", - "course_info = xmodule.html_block:CourseInfoBlock", - "customtag = xmodule.template_block:CustomTagBlock", - "custom_tag_template = xmodule.template_block:CustomTagTemplateBlock", - "discuss = xmodule.template_block:TranslateCustomTagBlock", - "discussion = xmodule.discussion_block:DiscussionXBlock", - "error = xmodule.error_block:ErrorBlock", - "hidden = xmodule.hidden_block:HiddenBlock", - "html = xmodule.html_block:HtmlBlock", - "itembank = xmodule.item_bank_block:ItemBankBlock", - "image = xmodule.template_block:TranslateCustomTagBlock", - "library = xmodule.library_root_xblock:LibraryRoot", - "library_content = xmodule.library_content_block:LegacyLibraryContentBlock", - "lti = xmodule.lti_block:LTIBlock", - "poll_question = xmodule.poll_block:PollBlock", - "problem = xmodule.capa_block:ProblemBlock", - "randomize = xmodule.randomize_block:RandomizeBlock", - "sequential = xmodule.seq_block:SequenceBlock", - "slides = xmodule.template_block:TranslateCustomTagBlock", - "split_test = xmodule.split_test_block:SplitTestBlock", - "static_tab = xmodule.html_block:StaticTabBlock", - "unit = xmodule.unit_block:UnitBlock", - "vertical = xmodule.vertical_block:VerticalBlock", - "video = xmodule.video_block:VideoBlock", - "videoalpha = xmodule.video_block:VideoBlock", - "videodev = xmodule.template_block:TranslateCustomTagBlock", - "word_cloud = xmodule.word_cloud_block:WordCloudBlock", - "wrapper = xmodule.wrapper_block:WrapperBlock", -] XBLOCKS_ASIDES = [ 'tagging_aside = cms.lib.xblock.tagging:StructuredTagsAside', ] @@ -170,7 +135,6 @@ 'enrollment_track = xmodule.partitions.enrollment_track_partition_generator:create_enrollment_track_partition', # lint-amnesty, pylint: disable=line-too-long 'team = openedx.core.lib.teams_config:create_team_set_partition', ], - 'xblock.v1': XBLOCKS, 'xblock_asides.v1': XBLOCKS_ASIDES, 'console_scripts': [ 'xmodule_assets = xmodule.static_content:main', From ea5c439d301843c78540c80bd3b5b38e97ce14fc Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 11:32:03 -0500 Subject: [PATCH 04/15] feat: migrate XBlock Asides entry points to pyproject.toml Migrates 1 xblock_asides.v1 entry point from setup.py. Removes XBLOCKS_ASIDES constant as it's no longer needed. Co-Authored-By: Claude Opus 4.5 --- pyproject.toml | 3 +++ setup.py | 6 ------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0b72676081dd..e461f30a7bf8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,3 +48,6 @@ videoalpha = "xmodule.video_block:VideoBlock" videodev = "xmodule.template_block:TranslateCustomTagBlock" word_cloud = "xmodule.word_cloud_block:WordCloudBlock" wrapper = "xmodule.wrapper_block:WrapperBlock" + +[project.entry-points."xblock_asides.v1"] +tagging_aside = "cms.lib.xblock.tagging:StructuredTagsAside" diff --git a/setup.py b/setup.py index dd92884efb9b..5c4ce476c461 100644 --- a/setup.py +++ b/setup.py @@ -4,11 +4,6 @@ from setuptools import setup -XBLOCKS_ASIDES = [ - 'tagging_aside = cms.lib.xblock.tagging:StructuredTagsAside', -] - - setup( entry_points={ "openedx.course_tab": [ @@ -135,7 +130,6 @@ 'enrollment_track = xmodule.partitions.enrollment_track_partition_generator:create_enrollment_track_partition', # lint-amnesty, pylint: disable=line-too-long 'team = openedx.core.lib.teams_config:create_team_set_partition', ], - 'xblock_asides.v1': XBLOCKS_ASIDES, 'console_scripts': [ 'xmodule_assets = xmodule.static_content:main', ], From 3d4dc1182dd98f3f6227d88de55985c1d756abf9 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 11:35:27 -0500 Subject: [PATCH 05/15] feat: migrate course tab entry points to pyproject.toml Migrates 19 openedx.course_tab entry points from setup.py. Co-Authored-By: Claude Opus 4.5 --- pyproject.toml | 21 +++++++++++++++++++++ setup.py | 21 --------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e461f30a7bf8..6befaddb1ac9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,3 +51,24 @@ wrapper = "xmodule.wrapper_block:WrapperBlock" [project.entry-points."xblock_asides.v1"] tagging_aside = "cms.lib.xblock.tagging:StructuredTagsAside" + +[project.entry-points."openedx.course_tab"] +ccx = "lms.djangoapps.ccx.plugins:CcxCourseTab" +courseware = "lms.djangoapps.courseware.tabs:CoursewareTab" +dates = "lms.djangoapps.courseware.tabs:DatesTab" +discussion = "lms.djangoapps.discussion.plugins:DiscussionTab" +edxnotes = "lms.djangoapps.edxnotes.plugins:EdxNotesTab" +external_discussion = "lms.djangoapps.courseware.tabs:ExternalDiscussionCourseTab" +external_link = "lms.djangoapps.courseware.tabs:ExternalLinkCourseTab" +html_textbooks = "lms.djangoapps.courseware.tabs:HtmlTextbookTabs" +instructor = "lms.djangoapps.instructor.views.instructor_dashboard:InstructorDashboardTab" +lti_discussion = "openedx.features.lti_course_tab.tab:DiscussionLtiCourseTab" +lti_live = "openedx.core.djangoapps.course_live.tab:CourseLiveTab" +lti_tab = "openedx.features.lti_course_tab.tab:LtiCourseTab" +pdf_textbooks = "lms.djangoapps.courseware.tabs:PDFTextbookTabs" +progress = "lms.djangoapps.courseware.tabs:ProgressTab" +static_tab = "xmodule.tabs:StaticTab" +syllabus = "lms.djangoapps.courseware.tabs:SyllabusTab" +teams = "lms.djangoapps.teams.plugins:TeamsTab" +textbooks = "lms.djangoapps.courseware.tabs:TextbookTabs" +wiki = "lms.djangoapps.course_wiki.tab:WikiTab" diff --git a/setup.py b/setup.py index 5c4ce476c461..c7e038c77ffe 100644 --- a/setup.py +++ b/setup.py @@ -6,27 +6,6 @@ setup( entry_points={ - "openedx.course_tab": [ - "ccx = lms.djangoapps.ccx.plugins:CcxCourseTab", - "courseware = lms.djangoapps.courseware.tabs:CoursewareTab", - "dates = lms.djangoapps.courseware.tabs:DatesTab", - "discussion = lms.djangoapps.discussion.plugins:DiscussionTab", - "edxnotes = lms.djangoapps.edxnotes.plugins:EdxNotesTab", - "external_discussion = lms.djangoapps.courseware.tabs:ExternalDiscussionCourseTab", - "external_link = lms.djangoapps.courseware.tabs:ExternalLinkCourseTab", - "html_textbooks = lms.djangoapps.courseware.tabs:HtmlTextbookTabs", - "instructor = lms.djangoapps.instructor.views.instructor_dashboard:InstructorDashboardTab", - "lti_discussion = openedx.features.lti_course_tab.tab:DiscussionLtiCourseTab", - "lti_live = openedx.core.djangoapps.course_live.tab:CourseLiveTab", - "lti_tab = openedx.features.lti_course_tab.tab:LtiCourseTab", - "pdf_textbooks = lms.djangoapps.courseware.tabs:PDFTextbookTabs", - "progress = lms.djangoapps.courseware.tabs:ProgressTab", - "static_tab = xmodule.tabs:StaticTab", - "syllabus = lms.djangoapps.courseware.tabs:SyllabusTab", - "teams = lms.djangoapps.teams.plugins:TeamsTab", - "textbooks = lms.djangoapps.courseware.tabs:TextbookTabs", - "wiki = lms.djangoapps.course_wiki.tab:WikiTab", - ], "openedx.course_app": [ "calculator = lms.djangoapps.courseware.plugins:CalculatorCourseApp", "custom_pages = lms.djangoapps.courseware.plugins:CustomPagesCourseApp", From 8ce02c8ab72720a528405825934f2967e6cf3c83 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 11:36:54 -0500 Subject: [PATCH 06/15] feat: migrate course app entry points to pyproject.toml Migrates 11 openedx.course_app entry points from setup.py. Co-Authored-By: Claude Opus 4.5 --- pyproject.toml | 13 +++++++++++++ setup.py | 13 ------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6befaddb1ac9..5b2dab52aa3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,3 +72,16 @@ syllabus = "lms.djangoapps.courseware.tabs:SyllabusTab" teams = "lms.djangoapps.teams.plugins:TeamsTab" textbooks = "lms.djangoapps.courseware.tabs:TextbookTabs" wiki = "lms.djangoapps.course_wiki.tab:WikiTab" + +[project.entry-points."openedx.course_app"] +calculator = "lms.djangoapps.courseware.plugins:CalculatorCourseApp" +custom_pages = "lms.djangoapps.courseware.plugins:CustomPagesCourseApp" +discussion = "openedx.core.djangoapps.discussions.plugins:DiscussionCourseApp" +edxnotes = "lms.djangoapps.edxnotes.plugins:EdxNotesCourseApp" +live = "openedx.core.djangoapps.course_live.plugins:LiveCourseApp" +ora_settings = "lms.djangoapps.courseware.plugins:ORASettingsApp" +proctoring = "lms.djangoapps.courseware.plugins:ProctoringCourseApp" +progress = "lms.djangoapps.courseware.plugins:ProgressCourseApp" +teams = "lms.djangoapps.teams.plugins:TeamsCourseApp" +textbooks = "lms.djangoapps.courseware.plugins:TextbooksCourseApp" +wiki = "lms.djangoapps.course_wiki.plugins.course_app:WikiCourseApp" diff --git a/setup.py b/setup.py index c7e038c77ffe..0030dc9ee601 100644 --- a/setup.py +++ b/setup.py @@ -6,19 +6,6 @@ setup( entry_points={ - "openedx.course_app": [ - "calculator = lms.djangoapps.courseware.plugins:CalculatorCourseApp", - "custom_pages = lms.djangoapps.courseware.plugins:CustomPagesCourseApp", - "discussion = openedx.core.djangoapps.discussions.plugins:DiscussionCourseApp", - "edxnotes = lms.djangoapps.edxnotes.plugins:EdxNotesCourseApp", - "live = openedx.core.djangoapps.course_live.plugins:LiveCourseApp", - "ora_settings = lms.djangoapps.courseware.plugins:ORASettingsApp", - "proctoring = lms.djangoapps.courseware.plugins:ProctoringCourseApp", - "progress = lms.djangoapps.courseware.plugins:ProgressCourseApp", - "teams = lms.djangoapps.teams.plugins:TeamsCourseApp", - "textbooks = lms.djangoapps.courseware.plugins:TextbooksCourseApp", - "wiki = lms.djangoapps.course_wiki.plugins.course_app:WikiCourseApp", - ], "openedx.course_tool": [ "calendar_sync_toggle = openedx.features.calendar_sync.plugins:CalendarSyncToggleTool", "course_bookmarks = openedx.features.course_bookmarks.plugins:CourseBookmarksTool", From ac854a01126d851c7b2fc95ae92776fdc5d37fb6 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 11:38:09 -0500 Subject: [PATCH 07/15] feat: migrate course tool entry points to pyproject.toml Migrates 4 openedx.course_tool entry points from setup.py. Co-Authored-By: Claude Opus 4.5 --- pyproject.toml | 6 ++++++ setup.py | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5b2dab52aa3d..522cb39eaf2b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,3 +85,9 @@ progress = "lms.djangoapps.courseware.plugins:ProgressCourseApp" teams = "lms.djangoapps.teams.plugins:TeamsCourseApp" textbooks = "lms.djangoapps.courseware.plugins:TextbooksCourseApp" wiki = "lms.djangoapps.course_wiki.plugins.course_app:WikiCourseApp" + +[project.entry-points."openedx.course_tool"] +calendar_sync_toggle = "openedx.features.calendar_sync.plugins:CalendarSyncToggleTool" +course_bookmarks = "openedx.features.course_bookmarks.plugins:CourseBookmarksTool" +course_updates = "openedx.features.course_experience.plugins:CourseUpdatesTool" +financial_assistance = "lms.djangoapps.courseware.course_tools:FinancialAssistanceTool" diff --git a/setup.py b/setup.py index 0030dc9ee601..f0faf901a7d5 100644 --- a/setup.py +++ b/setup.py @@ -6,12 +6,6 @@ setup( entry_points={ - "openedx.course_tool": [ - "calendar_sync_toggle = openedx.features.calendar_sync.plugins:CalendarSyncToggleTool", - "course_bookmarks = openedx.features.course_bookmarks.plugins:CourseBookmarksTool", - "course_updates = openedx.features.course_experience.plugins:CourseUpdatesTool", - "financial_assistance = lms.djangoapps.courseware.course_tools:FinancialAssistanceTool", - ], "openedx.user_partition_scheme": [ "cohort = openedx.core.djangoapps.course_groups.partition_scheme:CohortPartitionScheme", "content_type_gate = openedx.features.content_type_gating.partitions:ContentTypeGatingPartitionScheme", From a9ef2bb655f39e4aaba2af8a801afe68699c2a81 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 11:39:21 -0500 Subject: [PATCH 08/15] feat: migrate LMS Django app entry points to pyproject.toml Migrates 17 lms.djangoapp entry points from setup.py. Co-Authored-By: Claude Opus 4.5 --- pyproject.toml | 19 +++++++++++++++++++ setup.py | 19 ------------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 522cb39eaf2b..606357f69f9d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -91,3 +91,22 @@ calendar_sync_toggle = "openedx.features.calendar_sync.plugins:CalendarSyncToggl course_bookmarks = "openedx.features.course_bookmarks.plugins:CourseBookmarksTool" course_updates = "openedx.features.course_experience.plugins:CourseUpdatesTool" financial_assistance = "lms.djangoapps.courseware.course_tools:FinancialAssistanceTool" + +[project.entry-points."lms.djangoapp"] +ace_common = "openedx.core.djangoapps.ace_common.apps:AceCommonConfig" +content_libraries = "openedx.core.djangoapps.content_libraries.apps:ContentLibrariesConfig" +course_apps = "openedx.core.djangoapps.course_apps.apps:CourseAppsConfig" +course_live = "openedx.core.djangoapps.course_live.apps:CourseLiveConfig" +courseware_api = "openedx.core.djangoapps.courseware_api.apps:CoursewareAPIConfig" +credentials = "openedx.core.djangoapps.credentials.apps:CredentialsConfig" +discussion = "lms.djangoapps.discussion.apps:DiscussionConfig" +discussions = "openedx.core.djangoapps.discussions.apps:DiscussionsConfig" +grades = "lms.djangoapps.grades.apps:GradesConfig" +plugins = "openedx.core.djangoapps.plugins.apps:PluginsConfig" +theming = "openedx.core.djangoapps.theming.apps:ThemingConfig" +bookmarks = "openedx.core.djangoapps.bookmarks.apps:BookmarksConfig" +zendesk_proxy = "openedx.core.djangoapps.zendesk_proxy.apps:ZendeskProxyConfig" +instructor = "lms.djangoapps.instructor.apps:InstructorConfig" +password_policy = "openedx.core.djangoapps.password_policy.apps:PasswordPolicyConfig" +user_authn = "openedx.core.djangoapps.user_authn.apps:UserAuthnConfig" +program_enrollments = "lms.djangoapps.program_enrollments.apps:ProgramEnrollmentsConfig" diff --git a/setup.py b/setup.py index f0faf901a7d5..7b4453829075 100644 --- a/setup.py +++ b/setup.py @@ -41,25 +41,6 @@ "openedx.call_to_action": [ "personalized_learner_schedules = openedx.features.personalized_learner_schedules.call_to_action:PersonalizedLearnerScheduleCallToAction" # lint-amnesty, pylint: disable=line-too-long ], - "lms.djangoapp": [ - "ace_common = openedx.core.djangoapps.ace_common.apps:AceCommonConfig", - "content_libraries = openedx.core.djangoapps.content_libraries.apps:ContentLibrariesConfig", - "course_apps = openedx.core.djangoapps.course_apps.apps:CourseAppsConfig", - "course_live = openedx.core.djangoapps.course_live.apps:CourseLiveConfig", - "courseware_api = openedx.core.djangoapps.courseware_api.apps:CoursewareAPIConfig", - "credentials = openedx.core.djangoapps.credentials.apps:CredentialsConfig", - "discussion = lms.djangoapps.discussion.apps:DiscussionConfig", - "discussions = openedx.core.djangoapps.discussions.apps:DiscussionsConfig", - "grades = lms.djangoapps.grades.apps:GradesConfig", - "plugins = openedx.core.djangoapps.plugins.apps:PluginsConfig", - "theming = openedx.core.djangoapps.theming.apps:ThemingConfig", - "bookmarks = openedx.core.djangoapps.bookmarks.apps:BookmarksConfig", - "zendesk_proxy = openedx.core.djangoapps.zendesk_proxy.apps:ZendeskProxyConfig", - "instructor = lms.djangoapps.instructor.apps:InstructorConfig", - "password_policy = openedx.core.djangoapps.password_policy.apps:PasswordPolicyConfig", - "user_authn = openedx.core.djangoapps.user_authn.apps:UserAuthnConfig", - "program_enrollments = lms.djangoapps.program_enrollments.apps:ProgramEnrollmentsConfig", - ], "cms.djangoapp": [ "ace_common = openedx.core.djangoapps.ace_common.apps:AceCommonConfig", "bookmarks = openedx.core.djangoapps.bookmarks.apps:BookmarksConfig", From 3e420058249a9e049f2c3014d67caa45fc5d5d3a Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 11:41:29 -0500 Subject: [PATCH 09/15] feat: migrate CMS Django app entry points to pyproject.toml Migrates 14 cms.djangoapp entry points from setup.py. Preserves important comment explaining why discussion app (from LMS) is imported into Studio process. Co-Authored-By: Claude Opus 4.5 --- pyproject.toml | 22 ++++++++++++++++++++++ setup.py | 22 ---------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 606357f69f9d..69d24c1c79d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,3 +110,25 @@ instructor = "lms.djangoapps.instructor.apps:InstructorConfig" password_policy = "openedx.core.djangoapps.password_policy.apps:PasswordPolicyConfig" user_authn = "openedx.core.djangoapps.user_authn.apps:UserAuthnConfig" program_enrollments = "lms.djangoapps.program_enrollments.apps:ProgramEnrollmentsConfig" + +[project.entry-points."cms.djangoapp"] +ace_common = "openedx.core.djangoapps.ace_common.apps:AceCommonConfig" +bookmarks = "openedx.core.djangoapps.bookmarks.apps:BookmarksConfig" +course_live = "openedx.core.djangoapps.course_live.apps:CourseLiveConfig" +content_libraries = "openedx.core.djangoapps.content_libraries.apps:ContentLibrariesConfig" +content_staging = "openedx.core.djangoapps.content_staging.apps:ContentStagingAppConfig" +course_apps = "openedx.core.djangoapps.course_apps.apps:CourseAppsConfig" +# Importing an LMS app into the Studio process is not a good practice. +# We're ignoring this for Discussions here because its placement in LMS +# is a historical artifact. The eventual goal is to consolidate the multiple +# discussions-related Django apps and either put them in the openedx/ dir, +# or in another repo entirely. +discussion = "lms.djangoapps.discussion.apps:DiscussionConfig" +discussions = "openedx.core.djangoapps.discussions.apps:DiscussionsConfig" +instructor = "lms.djangoapps.instructor.apps:InstructorConfig" +olx_rest_api = "openedx.core.djangoapps.olx_rest_api.apps:OlxRestApiAppConfig" +password_policy = "openedx.core.djangoapps.password_policy.apps:PasswordPolicyConfig" +plugins = "openedx.core.djangoapps.plugins.apps:PluginsConfig" +theming = "openedx.core.djangoapps.theming.apps:ThemingConfig" +user_authn = "openedx.core.djangoapps.user_authn.apps:UserAuthnConfig" +zendesk_proxy = "openedx.core.djangoapps.zendesk_proxy.apps:ZendeskProxyConfig" diff --git a/setup.py b/setup.py index 7b4453829075..7f6a800e243c 100644 --- a/setup.py +++ b/setup.py @@ -41,28 +41,6 @@ "openedx.call_to_action": [ "personalized_learner_schedules = openedx.features.personalized_learner_schedules.call_to_action:PersonalizedLearnerScheduleCallToAction" # lint-amnesty, pylint: disable=line-too-long ], - "cms.djangoapp": [ - "ace_common = openedx.core.djangoapps.ace_common.apps:AceCommonConfig", - "bookmarks = openedx.core.djangoapps.bookmarks.apps:BookmarksConfig", - "course_live = openedx.core.djangoapps.course_live.apps:CourseLiveConfig", - "content_libraries = openedx.core.djangoapps.content_libraries.apps:ContentLibrariesConfig", - "content_staging = openedx.core.djangoapps.content_staging.apps:ContentStagingAppConfig", - "course_apps = openedx.core.djangoapps.course_apps.apps:CourseAppsConfig", - # Importing an LMS app into the Studio process is not a good - # practice. We're ignoring this for Discussions here because its - # placement in LMS is a historical artifact. The eventual goal is to - # consolidate the multiple discussions-related Django apps and - # either put them in the openedx/ dir, or in another repo entirely. - "discussion = lms.djangoapps.discussion.apps:DiscussionConfig", - "discussions = openedx.core.djangoapps.discussions.apps:DiscussionsConfig", - "instructor = lms.djangoapps.instructor.apps:InstructorConfig", - "olx_rest_api = openedx.core.djangoapps.olx_rest_api.apps:OlxRestApiAppConfig", - "password_policy = openedx.core.djangoapps.password_policy.apps:PasswordPolicyConfig", - "plugins = openedx.core.djangoapps.plugins.apps:PluginsConfig", - "theming = openedx.core.djangoapps.theming.apps:ThemingConfig", - "user_authn = openedx.core.djangoapps.user_authn.apps:UserAuthnConfig", - "zendesk_proxy = openedx.core.djangoapps.zendesk_proxy.apps:ZendeskProxyConfig", - ], 'openedx.learning_context': [ 'lib = openedx.core.djangoapps.content_libraries.library_context:LibraryContextImpl', ], From 8ccc3bb81ac210de6973d22b815f01f2b4afa844 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 11:43:06 -0500 Subject: [PATCH 10/15] feat: migrate block transformer entry points to pyproject.toml Migrates 17 openedx.block_structure_transformer entry points from setup.py. Co-Authored-By: Claude Opus 4.5 --- pyproject.toml | 19 +++++++++++++++++++ setup.py | 19 ------------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 69d24c1c79d9..f39555e863ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -132,3 +132,22 @@ plugins = "openedx.core.djangoapps.plugins.apps:PluginsConfig" theming = "openedx.core.djangoapps.theming.apps:ThemingConfig" user_authn = "openedx.core.djangoapps.user_authn.apps:UserAuthnConfig" zendesk_proxy = "openedx.core.djangoapps.zendesk_proxy.apps:ZendeskProxyConfig" + +[project.entry-points."openedx.block_structure_transformer"] +library_content = "lms.djangoapps.course_blocks.transformers.library_content:ContentLibraryTransformer" +library_content_randomize = "lms.djangoapps.course_blocks.transformers.library_content:ContentLibraryOrderTransformer" +split_test = "lms.djangoapps.course_blocks.transformers.split_test:SplitTestTransformer" +start_date = "lms.djangoapps.course_blocks.transformers.start_date:StartDateTransformer" +user_partitions = "lms.djangoapps.course_blocks.transformers.user_partitions:UserPartitionTransformer" +visibility = "lms.djangoapps.course_blocks.transformers.visibility:VisibilityTransformer" +hidden_content = "lms.djangoapps.course_blocks.transformers.hidden_content:HiddenContentTransformer" +course_blocks_api = "lms.djangoapps.course_api.blocks.transformers.blocks_api:BlocksAPITransformer" +milestones = "lms.djangoapps.course_api.blocks.transformers.milestones:MilestonesAndSpecialExamsTransformer" +grades = "lms.djangoapps.grades.transformer:GradesTransformer" +completion = "lms.djangoapps.course_api.blocks.transformers.block_completion:BlockCompletionTransformer" +load_override_data = "lms.djangoapps.course_blocks.transformers.load_override_data:OverrideDataTransformer" +content_type_gate = "openedx.features.content_type_gating.block_transformers:ContentTypeGateTransformer" +access_denied_message_filter = "lms.djangoapps.course_blocks.transformers.access_denied_filter:AccessDeniedMessageFilterTransformer" +open_assessment_transformer = "lms.djangoapps.courseware.transformers:OpenAssessmentDateTransformer" +effort_estimation = "openedx.features.effort_estimation.api:EffortEstimationTransformer" +discussions_link = "openedx.core.djangoapps.discussions.transformers:DiscussionsTopicLinkTransformer" diff --git a/setup.py b/setup.py index 7f6a800e243c..b62d28ad1448 100644 --- a/setup.py +++ b/setup.py @@ -14,25 +14,6 @@ "team = lms.djangoapps.teams.team_partition_scheme:TeamPartitionScheme", "verification = openedx.core.djangoapps.user_api.partition_schemes:ReturnGroup1PartitionScheme", ], - "openedx.block_structure_transformer": [ - "library_content = lms.djangoapps.course_blocks.transformers.library_content:ContentLibraryTransformer", - "library_content_randomize = lms.djangoapps.course_blocks.transformers.library_content:ContentLibraryOrderTransformer", # lint-amnesty, pylint: disable=line-too-long - "split_test = lms.djangoapps.course_blocks.transformers.split_test:SplitTestTransformer", - "start_date = lms.djangoapps.course_blocks.transformers.start_date:StartDateTransformer", - "user_partitions = lms.djangoapps.course_blocks.transformers.user_partitions:UserPartitionTransformer", - "visibility = lms.djangoapps.course_blocks.transformers.visibility:VisibilityTransformer", - "hidden_content = lms.djangoapps.course_blocks.transformers.hidden_content:HiddenContentTransformer", - "course_blocks_api = lms.djangoapps.course_api.blocks.transformers.blocks_api:BlocksAPITransformer", - "milestones = lms.djangoapps.course_api.blocks.transformers.milestones:MilestonesAndSpecialExamsTransformer", # lint-amnesty, pylint: disable=line-too-long - "grades = lms.djangoapps.grades.transformer:GradesTransformer", - "completion = lms.djangoapps.course_api.blocks.transformers.block_completion:BlockCompletionTransformer", - "load_override_data = lms.djangoapps.course_blocks.transformers.load_override_data:OverrideDataTransformer", - "content_type_gate = openedx.features.content_type_gating.block_transformers:ContentTypeGateTransformer", - "access_denied_message_filter = lms.djangoapps.course_blocks.transformers.access_denied_filter:AccessDeniedMessageFilterTransformer", # lint-amnesty, pylint: disable=line-too-long - "open_assessment_transformer = lms.djangoapps.courseware.transformers:OpenAssessmentDateTransformer", - 'effort_estimation = openedx.features.effort_estimation.api:EffortEstimationTransformer', - 'discussions_link = openedx.core.djangoapps.discussions.transformers:DiscussionsTopicLinkTransformer', - ], "openedx.ace.policy": [ "bulk_email_optout = lms.djangoapps.bulk_email.policies:CourseEmailOptout", "course_push_notification_optout = openedx.core.djangoapps.notifications.policies:CoursePushNotificationOptout", # lint-amnesty, pylint: disable=line-too-long From 327a1f8d5b67bf9eafb38fa4ac832da751b649c5 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 11:45:08 -0500 Subject: [PATCH 11/15] feat: migrate remaining plugin entry points to pyproject.toml Migrates final 6 entry point groups from setup.py (user_partition_scheme, ace.policy, call_to_action, learning_context, dynamic_partition_generator, console_scripts). Simplifies setup.py to just call setup() with no arguments - all entry points now defined in pyproject.toml. Co-Authored-By: Claude Opus 4.5 --- pyproject.toml | 27 +++++++++++++++++++++++++++ setup.py | 32 +------------------------------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f39555e863ba..4ffa2965e5d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -151,3 +151,30 @@ access_denied_message_filter = "lms.djangoapps.course_blocks.transformers.access open_assessment_transformer = "lms.djangoapps.courseware.transformers:OpenAssessmentDateTransformer" effort_estimation = "openedx.features.effort_estimation.api:EffortEstimationTransformer" discussions_link = "openedx.core.djangoapps.discussions.transformers:DiscussionsTopicLinkTransformer" + +[project.entry-points."openedx.user_partition_scheme"] +cohort = "openedx.core.djangoapps.course_groups.partition_scheme:CohortPartitionScheme" +content_type_gate = "openedx.features.content_type_gating.partitions:ContentTypeGatingPartitionScheme" +enrollment_track = "openedx.core.djangoapps.verified_track_content.partition_scheme:EnrollmentTrackPartitionScheme" +random = "openedx.core.djangoapps.user_api.partition_schemes:RandomUserPartitionScheme" +team = "lms.djangoapps.teams.team_partition_scheme:TeamPartitionScheme" +verification = "openedx.core.djangoapps.user_api.partition_schemes:ReturnGroup1PartitionScheme" + +[project.entry-points."openedx.ace.policy"] +bulk_email_optout = "lms.djangoapps.bulk_email.policies:CourseEmailOptout" +course_push_notification_optout = "openedx.core.djangoapps.notifications.policies:CoursePushNotificationOptout" +disabled_user_optout = "openedx.core.djangoapps.ace_common.policies:DisableUserOptout" + +[project.entry-points."openedx.call_to_action"] +personalized_learner_schedules = "openedx.features.personalized_learner_schedules.call_to_action:PersonalizedLearnerScheduleCallToAction" + +[project.entry-points."openedx.learning_context"] +lib = "openedx.core.djangoapps.content_libraries.library_context:LibraryContextImpl" + +[project.entry-points."openedx.dynamic_partition_generator"] +content_type_gating = "openedx.features.content_type_gating.partitions:create_content_gating_partition" +enrollment_track = "xmodule.partitions.enrollment_track_partition_generator:create_enrollment_track_partition" +team = "openedx.core.lib.teams_config:create_team_set_partition" + +[project.entry-points.console_scripts] +xmodule_assets = "xmodule.static_content:main" diff --git a/setup.py b/setup.py index b62d28ad1448..6bc0c08d65d7 100644 --- a/setup.py +++ b/setup.py @@ -4,34 +4,4 @@ from setuptools import setup -setup( - entry_points={ - "openedx.user_partition_scheme": [ - "cohort = openedx.core.djangoapps.course_groups.partition_scheme:CohortPartitionScheme", - "content_type_gate = openedx.features.content_type_gating.partitions:ContentTypeGatingPartitionScheme", - "enrollment_track = openedx.core.djangoapps.verified_track_content.partition_scheme:EnrollmentTrackPartitionScheme", # lint-amnesty, pylint: disable=line-too-long - "random = openedx.core.djangoapps.user_api.partition_schemes:RandomUserPartitionScheme", - "team = lms.djangoapps.teams.team_partition_scheme:TeamPartitionScheme", - "verification = openedx.core.djangoapps.user_api.partition_schemes:ReturnGroup1PartitionScheme", - ], - "openedx.ace.policy": [ - "bulk_email_optout = lms.djangoapps.bulk_email.policies:CourseEmailOptout", - "course_push_notification_optout = openedx.core.djangoapps.notifications.policies:CoursePushNotificationOptout", # lint-amnesty, pylint: disable=line-too-long - "disabled_user_optout = openedx.core.djangoapps.ace_common.policies:DisableUserOptout", - ], - "openedx.call_to_action": [ - "personalized_learner_schedules = openedx.features.personalized_learner_schedules.call_to_action:PersonalizedLearnerScheduleCallToAction" # lint-amnesty, pylint: disable=line-too-long - ], - 'openedx.learning_context': [ - 'lib = openedx.core.djangoapps.content_libraries.library_context:LibraryContextImpl', - ], - 'openedx.dynamic_partition_generator': [ - 'content_type_gating = openedx.features.content_type_gating.partitions:create_content_gating_partition', - 'enrollment_track = xmodule.partitions.enrollment_track_partition_generator:create_enrollment_track_partition', # lint-amnesty, pylint: disable=line-too-long - 'team = openedx.core.lib.teams_config:create_team_set_partition', - ], - 'console_scripts': [ - 'xmodule_assets = xmodule.static_content:main', - ], - } -) +setup() From baabe2f0be0ece4f4f737bf585e88ca35657758a Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 11:50:29 -0500 Subject: [PATCH 12/15] feat: migrate pytest configuration to pyproject.toml Migrates pytest configuration from setup.cfg, preserving all settings and comments including warning filters and test discovery patterns. Co-Authored-By: Claude Opus 4.5 --- pyproject.toml | 25 +++++++++++++++++++++++++ setup.cfg | 26 -------------------------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4ffa2965e5d3..a43c535f7539 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -178,3 +178,28 @@ team = "openedx.core.lib.teams_config:create_team_set_partition" [project.entry-points.console_scripts] xmodule_assets = "xmodule.static_content:main" + +[tool.pytest.ini_options] +# Note: The first file of settings found is used, there is no combining, so +# this file is only used for tests that don't find a pytest.ini file first. +# Details at https://docs.pytest.org/en/latest/reference/customize.html +DJANGO_SETTINGS_MODULE = "lms.envs.test" +addopts = "--nomigrations --reuse-db --durations=20 --json-report --json-report-omit keywords streams collectors log traceback tests --json-report-file=none" +# Enable default handling for all warnings, including those that are ignored by default; +# but hide rate-limit warnings (because we deliberately don't throttle test user logins) +# and field_data deprecation warnings (because fixing them requires a major low-priority refactoring) +filterwarnings = [ + "default", + "ignore:No request passed to the backend, unable to rate-limit:UserWarning", + "ignore::xblock.exceptions.FieldDataDeprecationWarning", + # Remove default_app_config warning after updating Django to 4.2 + "ignore:.*You can remove default_app_config.*:PendingDeprecationWarning", + "ignore:Instead access HTTPResponse.headers directly.*:DeprecationWarning:elasticsearch", + # ABC deprecation Warning comes from libsass + "ignore:Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated.*:DeprecationWarning:sass", + "ignore:'etree' is deprecated. Use 'xml.etree.ElementTree' instead.:DeprecationWarning:wiki", +] +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"] diff --git a/setup.cfg b/setup.cfg index ac0907b667c3..f853b20be539 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,29 +1,3 @@ -[tool:pytest] -# Note: The first file of settings found is used, there is no combining, so -# this file is only used for tests that don't find a pytest.ini file first. -# Details at https://docs.pytest.org/en/latest/reference/customize.html - -DJANGO_SETTINGS_MODULE = lms.envs.test -addopts = --nomigrations --reuse-db --durations=20 --json-report --json-report-omit keywords streams collectors log traceback tests --json-report-file=none -# Enable default handling for all warnings, including those that are ignored by default; -# but hide rate-limit warnings (because we deliberately don't throttle test user logins) -# and field_data deprecation warnings (because fixing them requires a major low-priority refactoring) -filterwarnings = - default - ignore:No request passed to the backend, unable to rate-limit:UserWarning - ignore::xblock.exceptions.FieldDataDeprecationWarning - # Remove default_app_config warning after updating Django to 4.2 - ignore:.*You can remove default_app_config.*:PendingDeprecationWarning - ignore:Instead access HTTPResponse.headers directly.*:DeprecationWarning:elasticsearch - # ABC deprecation Warning comes from libsass - ignore:Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated.*:DeprecationWarning:sass - ignore:'etree' is deprecated. Use 'xml.etree.ElementTree' instead.:DeprecationWarning:wiki - -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 - [pycodestyle] # error codes: https://pycodestyle.readthedocs.io/en/latest/intro.html#error-codes # E501: line too long From 27a68f670a2679a053b87b833fc26f680c2b4da1 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 11:53:56 -0500 Subject: [PATCH 13/15] 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 --- pyproject.toml | 192 ++++++++++++++++++++++++++++++++++++++++++++++ setup.cfg | 202 ------------------------------------------------- 2 files changed, 192 insertions(+), 202 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a43c535f7539..ad14aaf08b7a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -203,3 +203,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_learning"] +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 import apps from openedx-learning (only import from openedx_learning.api.* and openedx_learning.lib.*)." +type = "forbidden" +source_modules = ["cms", "lms", "openedx"] +forbidden_modules = ["openedx_learning.apps"] +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", +] diff --git a/setup.cfg b/setup.cfg index f853b20be539..7e1ab370a043 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,205 +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_learning -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 import apps from openedx-learning (only import from openedx_learning.api.* and openedx_learning.lib.*). -type = forbidden -source_modules = - cms - lms - openedx -forbidden_modules = - openedx_learning.apps -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 From 79887f03d359fc9d43b9327cb96e12c615e34eb3 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Tue, 20 Jan 2026 12:05:10 -0500 Subject: [PATCH 14/15] feat: delete setup.py - migration complete Removes setup.py as all configuration has been migrated to pyproject.toml (PEP 621). Verification confirms: - Package installs correctly with pip install -e . - All 142 entry points discoverable via importlib.metadata - All tool configurations (pytest, pycodestyle, isort, importlinter) working - Django checks pass for both LMS and CMS Co-Authored-By: Claude Opus 4.5 --- setup.py | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 setup.py diff --git a/setup.py b/setup.py deleted file mode 100644 index 6bc0c08d65d7..000000000000 --- a/setup.py +++ /dev/null @@ -1,7 +0,0 @@ -""" # lint-amnesty, pylint: disable=django-not-configured -Setup script for the Open edX package. -""" - -from setuptools import setup - -setup() From 21968c5fb2c163227328ebf57251d32663edf658 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Mon, 26 Jan 2026 15:56:50 -0500 Subject: [PATCH 15/15] docs: update references from setup.py to pyproject.toml Update documentation, comments, and docstrings throughout the codebase to reflect the migration from setup.py to pyproject.toml: - Transformer class docstrings: changed to reference "entry point name in the package configuration" for better future-proofing - Block structure module docs: updated to reference pyproject.toml - Test file comments: updated entry point references - Config files (tox.ini, pytest.ini): updated references - Documentation (extension_points.rst, course apps ADRs): updated to reference pyproject.toml with inclusive language for external packages - Requirements documentation (github.in): updated with inclusive language - edxmako README: modernized install command to use pip install Historical ADRs and references to external packages that may still use setup.py were intentionally left unchanged or updated with inclusive language acknowledging both pyproject.toml and setup.py. Co-Authored-By: Claude Opus 4.5 --- cms/pytest.ini | 2 +- common/djangoapps/edxmako/README | 2 +- common/test/pytest.ini | 2 +- docs/concepts/extension_points.rst | 2 +- .../transformers/access_denied_filter.py | 4 ++-- .../transformers/hidden_content.py | 4 ++-- .../transformers/library_content.py | 8 ++++---- .../transformers/load_override_data.py | 4 ++-- .../course_blocks/transformers/split_test.py | 4 ++-- .../course_blocks/transformers/start_date.py | 4 ++-- .../transformers/user_partitions.py | 4 ++-- .../course_blocks/transformers/visibility.py | 4 ++-- lms/djangoapps/courseware/transformers.py | 4 ++-- lms/djangoapps/grades/transformer.py | 4 ++-- .../content/block_structure/__init__.py | 2 +- .../content/block_structure/transformer.py | 2 +- .../docs/decisions/001-course-apps.rst | 4 ++-- .../tests/test_partition_scheme.py | 2 +- .../docs/decisions/001-course-live.rst | 6 +++--- .../djangoapps/discussions/transformers.py | 4 ++-- .../user_api/tests/test_partition_schemes.py | 2 +- .../tests/test_partition_scheme.py | 2 +- openedx/core/lib/xblock_pipeline/finder.py | 2 +- .../content_type_gating/block_transformers.py | 4 ++-- .../effort_estimation/block_transformers.py | 5 ++++- .../test_external_xblocks.py | 6 +++--- requirements/edx/github.in | 18 ++++++++++-------- tox.ini | 2 +- xmodule/partitions/partitions.py | 2 +- 29 files changed, 60 insertions(+), 55 deletions(-) diff --git a/cms/pytest.ini b/cms/pytest.ini index ee9263d63c27..368ceb257099 100644 --- a/cms/pytest.ini +++ b/cms/pytest.ini @@ -1,6 +1,6 @@ [pytest] # Note: The first file of settings found is used, there is no combining, so -# this file is used for the tests in the cms tree, and setup.cfg is ignored. +# this file is used for the tests in the cms tree, and pyproject.toml is ignored. # Details at https://docs.pytest.org/en/latest/reference/customize.html DJANGO_SETTINGS_MODULE = cms.envs.test diff --git a/common/djangoapps/edxmako/README b/common/djangoapps/edxmako/README index 9896d78747c3..03674276986f 100644 --- a/common/djangoapps/edxmako/README +++ b/common/djangoapps/edxmako/README @@ -19,5 +19,5 @@ Mako: http://www.makotemplates.org/ How to install? ================================================================================ - $ sudo python setup.py install + $ pip install . diff --git a/common/test/pytest.ini b/common/test/pytest.ini index d93a7421722b..29e51b489f4a 100644 --- a/common/test/pytest.ini +++ b/common/test/pytest.ini @@ -1,6 +1,6 @@ [pytest] # Note: The first file of settings found is used, there is no combining, so -# this file is used for the tests in the common/test tree, and setup.cfg is ignored. +# this file is used for the tests in the common/test tree, and pyproject.toml is ignored. # Details at https://docs.pytest.org/en/latest/reference/customize.html addopts = -p no:randomly --durations=20 --json-report --json-report-omit keywords streams collectors log traceback tests --json-report-file=none diff --git a/docs/concepts/extension_points.rst b/docs/concepts/extension_points.rst index ac70edc43209..a64e2e58ff59 100644 --- a/docs/concepts/extension_points.rst +++ b/docs/concepts/extension_points.rst @@ -87,7 +87,7 @@ If you wish to customize aspects of the learner or educator experiences, you'll Most python plugins are enabled using one of two methods: -1. A Python Entry point: the core Open edX platform provides a standard plugin loading mechanism in |edx_django_utils.plugins|_ which uses `stevedore`_ to find all installed python packages that declare a specific "entry point" in their setup.py file. See the ``entry_points`` defined in edx-platform's own ``setup.py`` for examples. +1. A Python Entry point: the core Open edX platform provides a standard plugin loading mechanism in |edx_django_utils.plugins|_ which uses `stevedore`_ to find all installed python packages that declare a specific "entry point" in their package configuration (typically ``pyproject.toml`` or ``setup.py``). See the ``entry_points`` defined in edx-platform's own ``pyproject.toml`` for examples. 2. A Django setting: Some plugins require modification of Django settings, which is typically done by editing ``/edx/etc/lms.yml`` (in Production) or ``edx-platform/lms/envs/private.py`` (on Devstack). .. |edx_django_utils.plugins| replace:: ``edx_django_utils.plugins`` diff --git a/lms/djangoapps/course_blocks/transformers/access_denied_filter.py b/lms/djangoapps/course_blocks/transformers/access_denied_filter.py index b4115dd755bb..8b6a531d5162 100644 --- a/lms/djangoapps/course_blocks/transformers/access_denied_filter.py +++ b/lms/djangoapps/course_blocks/transformers/access_denied_filter.py @@ -18,8 +18,8 @@ class AccessDeniedMessageFilterTransformer(BlockStructureTransformer): @classmethod def name(cls): """ - Unique identifier for the transformer's class; - same identifier used in setup.py. + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. """ return "access_denied_message_filter" diff --git a/lms/djangoapps/course_blocks/transformers/hidden_content.py b/lms/djangoapps/course_blocks/transformers/hidden_content.py index e82d8d3441d6..80e602e6f29d 100644 --- a/lms/djangoapps/course_blocks/transformers/hidden_content.py +++ b/lms/djangoapps/course_blocks/transformers/hidden_content.py @@ -39,8 +39,8 @@ class HiddenContentTransformer(BlockStructureTransformer): @classmethod def name(cls): """ - Unique identifier for the transformer's class; - same identifier used in setup.py. + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. """ return "hidden_content" diff --git a/lms/djangoapps/course_blocks/transformers/library_content.py b/lms/djangoapps/course_blocks/transformers/library_content.py index 10ef8c2138b6..e7f2258483bc 100644 --- a/lms/djangoapps/course_blocks/transformers/library_content.py +++ b/lms/djangoapps/course_blocks/transformers/library_content.py @@ -36,8 +36,8 @@ class ContentLibraryTransformer(FilteringTransformerMixin, BlockStructureTransfo @classmethod def name(cls): """ - Unique identifier for the transformer's class; - same identifier used in setup.py. + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. """ return "library_content" @@ -197,8 +197,8 @@ class ContentLibraryOrderTransformer(BlockStructureTransformer): @classmethod def name(cls): """ - Unique identifier for the transformer's class; - same identifier used in setup.py + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. """ return "library_content_randomize" diff --git a/lms/djangoapps/course_blocks/transformers/load_override_data.py b/lms/djangoapps/course_blocks/transformers/load_override_data.py index 672922ea510d..7f57768b5069 100644 --- a/lms/djangoapps/course_blocks/transformers/load_override_data.py +++ b/lms/djangoapps/course_blocks/transformers/load_override_data.py @@ -67,8 +67,8 @@ def __init__(self, user): @classmethod def name(cls): """ - Unique identifier for the transformer's class; - same identifier used in setup.py. + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. """ return "load_override_data" diff --git a/lms/djangoapps/course_blocks/transformers/split_test.py b/lms/djangoapps/course_blocks/transformers/split_test.py index 4de70d8badff..45fdaa09813d 100644 --- a/lms/djangoapps/course_blocks/transformers/split_test.py +++ b/lms/djangoapps/course_blocks/transformers/split_test.py @@ -30,8 +30,8 @@ class SplitTestTransformer(FilteringTransformerMixin, BlockStructureTransformer) @classmethod def name(cls): """ - Unique identifier for the transformer's class; - same identifier used in setup.py. + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. """ return "split_test" diff --git a/lms/djangoapps/course_blocks/transformers/start_date.py b/lms/djangoapps/course_blocks/transformers/start_date.py index 13ba3b7470c2..0d7165034efe 100644 --- a/lms/djangoapps/course_blocks/transformers/start_date.py +++ b/lms/djangoapps/course_blocks/transformers/start_date.py @@ -39,8 +39,8 @@ class StartDateTransformer(FilteringTransformerMixin, BlockStructureTransformer) @classmethod def name(cls): """ - Unique identifier for the transformer's class; - same identifier used in setup.py. + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. """ return "start_date" diff --git a/lms/djangoapps/course_blocks/transformers/user_partitions.py b/lms/djangoapps/course_blocks/transformers/user_partitions.py index 087c6e5fc7e7..54424eb06fbd 100644 --- a/lms/djangoapps/course_blocks/transformers/user_partitions.py +++ b/lms/djangoapps/course_blocks/transformers/user_partitions.py @@ -32,8 +32,8 @@ class UserPartitionTransformer(BlockStructureTransformer): @classmethod def name(cls): """ - Unique identifier for the transformer's class; - same identifier used in setup.py. + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. """ return "user_partitions" diff --git a/lms/djangoapps/course_blocks/transformers/visibility.py b/lms/djangoapps/course_blocks/transformers/visibility.py index 19b0d2252d80..f7dd0da5f0af 100644 --- a/lms/djangoapps/course_blocks/transformers/visibility.py +++ b/lms/djangoapps/course_blocks/transformers/visibility.py @@ -32,8 +32,8 @@ class VisibilityTransformer(FilteringTransformerMixin, BlockStructureTransformer @classmethod def name(cls): """ - Unique identifier for the transformer's class; - same identifier used in setup.py. + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. """ return "visibility" diff --git a/lms/djangoapps/courseware/transformers.py b/lms/djangoapps/courseware/transformers.py index 2577a9c42331..f2bba80c2ba1 100644 --- a/lms/djangoapps/courseware/transformers.py +++ b/lms/djangoapps/courseware/transformers.py @@ -18,8 +18,8 @@ class OpenAssessmentDateTransformer(FilteringTransformerMixin, BlockStructureTra @classmethod def name(cls): """ - Unique identifier for the transformer's class; - same identifier used in setup.py. + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. """ return 'open_assessment_transformer' diff --git a/lms/djangoapps/grades/transformer.py b/lms/djangoapps/grades/transformer.py index 38fe903ba7d9..f322ea0162e5 100644 --- a/lms/djangoapps/grades/transformer.py +++ b/lms/djangoapps/grades/transformer.py @@ -55,8 +55,8 @@ class GradesTransformer(BlockStructureTransformer): @classmethod def name(cls): """ - Unique identifier for the transformer's class; - same identifier used in setup.py. + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. """ return 'grades' diff --git a/openedx/core/djangoapps/content/block_structure/__init__.py b/openedx/core/djangoapps/content/block_structure/__init__.py index 00ed1b12d2ef..cf345b73cf51 100644 --- a/openedx/core/djangoapps/content/block_structure/__init__.py +++ b/openedx/core/djangoapps/content/block_structure/__init__.py @@ -49,7 +49,7 @@ Registry. Transformers are registered using the platform's PluginManager (e.g., Stevedore). This is currently done by updating -setup.py. Only registered transformers are called during the Collect +pyproject.toml. Only registered transformers are called during the Collect Phase. And only registered transformers can be used during the Transform phase. Exceptions to this rule are any nested transformers that are contained within higher-order transformers - as long as the diff --git a/openedx/core/djangoapps/content/block_structure/transformer.py b/openedx/core/djangoapps/content/block_structure/transformer.py index 1bcde62e0650..b2ac620dc9a1 100644 --- a/openedx/core/djangoapps/content/block_structure/transformer.py +++ b/openedx/core/djangoapps/content/block_structure/transformer.py @@ -65,7 +65,7 @@ def name(cls): identify the transformer's cached data. So it should be unique and not conflict with other transformers. Consider using the same name that is used in the Transformer Registry. For example, - for Stevedore, it is specified in the setup.py file. + for Stevedore, it is specified in the package configuration (pyproject.toml). Once the transformer is in use and its data is cached, do not modify this name value without consideration of backward diff --git a/openedx/core/djangoapps/course_apps/docs/decisions/001-course-apps.rst b/openedx/core/djangoapps/course_apps/docs/decisions/001-course-apps.rst index 4491779a2751..cccd0eba8776 100644 --- a/openedx/core/djangoapps/course_apps/docs/decisions/001-course-apps.rst +++ b/openedx/core/djangoapps/course_apps/docs/decisions/001-course-apps.rst @@ -105,14 +105,14 @@ check. Course App Plugin Class ======================= -To be loaded as a Course App, you need to provide an entrypoint in ``setup.py`` +To be loaded as a Course App, you need to provide an entrypoint in ``pyproject.toml`` with the namespace ``openedx.course_app``. The entry should point to a Python class with the following basic structure: .. code-block:: python class CourseApp: - # The app id should match what is specified in the setup.py entrypoint + # The app id should match what is specified in the pyproject.toml entrypoint app_id: str = 'wiki' name: str = 'Wiki' description: str = 'A short description of what the Wiki does.' diff --git a/openedx/core/djangoapps/course_groups/tests/test_partition_scheme.py b/openedx/core/djangoapps/course_groups/tests/test_partition_scheme.py index 73f824d9fab5..1b6a4bdb18ae 100644 --- a/openedx/core/djangoapps/course_groups/tests/test_partition_scheme.py +++ b/openedx/core/djangoapps/course_groups/tests/test_partition_scheme.py @@ -254,7 +254,7 @@ def test_missing_partition(self): class TestExtension(django.test.TestCase): """ Ensure that the scheme extension is correctly plugged in (via entry point - in setup.py) + in pyproject.toml) """ def test_get_scheme(self): diff --git a/openedx/core/djangoapps/course_live/docs/decisions/001-course-live.rst b/openedx/core/djangoapps/course_live/docs/decisions/001-course-live.rst index 27ee3b488482..9fbed126c654 100644 --- a/openedx/core/djangoapps/course_live/docs/decisions/001-course-live.rst +++ b/openedx/core/djangoapps/course_live/docs/decisions/001-course-live.rst @@ -29,10 +29,10 @@ Decision We proposed to add the course live as a plugin with the following structure Course Live App Plugin Class ------------------------ +---------------------------- -The app will be loaded as a plugin and added to `setup.py` with the namespace -"openedx.course_app". +The app will be loaded as a plugin and added to ``pyproject.toml`` with the namespace +``openedx.course_app``. .. code-block :: python diff --git a/openedx/core/djangoapps/discussions/transformers.py b/openedx/core/djangoapps/discussions/transformers.py index 8fcf78bc1f98..a4d34434ab12 100644 --- a/openedx/core/djangoapps/discussions/transformers.py +++ b/openedx/core/djangoapps/discussions/transformers.py @@ -19,8 +19,8 @@ class DiscussionsTopicLinkTransformer(BlockStructureTransformer): @classmethod def name(cls): """ - Unique identifier for the transformer's class; - same identifier used in setup.py. + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. """ return "discussions_link" diff --git a/openedx/core/djangoapps/user_api/tests/test_partition_schemes.py b/openedx/core/djangoapps/user_api/tests/test_partition_schemes.py index b1fc0e6e577f..afa6ab7939db 100644 --- a/openedx/core/djangoapps/user_api/tests/test_partition_schemes.py +++ b/openedx/core/djangoapps/user_api/tests/test_partition_schemes.py @@ -141,7 +141,7 @@ def test_change_group_name(self): class TestExtension(TestCase): """ Ensure that the scheme extension is correctly plugged in (via entry point - in setup.py) + in pyproject.toml) """ def test_get_scheme(self): diff --git a/openedx/core/djangoapps/verified_track_content/tests/test_partition_scheme.py b/openedx/core/djangoapps/verified_track_content/tests/test_partition_scheme.py index f7de6042ef19..b30eea70d439 100644 --- a/openedx/core/djangoapps/verified_track_content/tests/test_partition_scheme.py +++ b/openedx/core/djangoapps/verified_track_content/tests/test_partition_scheme.py @@ -94,7 +94,7 @@ def setUpClass(cls): def test_get_scheme(self): """ - Ensure that the scheme extension is correctly plugged in (via entry point in setup.py) + Ensure that the scheme extension is correctly plugged in (via entry point in pyproject.toml) """ assert UserPartition.get_scheme('enrollment_track') == EnrollmentTrackPartitionScheme diff --git a/openedx/core/lib/xblock_pipeline/finder.py b/openedx/core/lib/xblock_pipeline/finder.py index a87b564afce1..6c6791f708d4 100644 --- a/openedx/core/lib/xblock_pipeline/finder.py +++ b/openedx/core/lib/xblock_pipeline/finder.py @@ -123,7 +123,7 @@ def __init__(self, *args, **kwargs): # xblock_resource_info holds (package_name, resources_dir) tuples. While # it never happens in practice, the XBlock API does allow different - # XBlocks installed with the same setup.py to refer to their shared + # XBlocks installed from the same package to refer to their shared # static assets using different prefixes. xblock_resource_info = { (xblock_resource_pkg(xblock_class), xblock_class.get_resources_dir()) diff --git a/openedx/features/content_type_gating/block_transformers.py b/openedx/features/content_type_gating/block_transformers.py index 94b66eda4367..b374e3ff5174 100644 --- a/openedx/features/content_type_gating/block_transformers.py +++ b/openedx/features/content_type_gating/block_transformers.py @@ -25,8 +25,8 @@ class ContentTypeGateTransformer(BlockStructureTransformer): @classmethod def name(cls): """ - Unique identifier for the transformer's class; - same identifier used in setup.py. + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. """ return "content_type_gate" diff --git a/openedx/features/effort_estimation/block_transformers.py b/openedx/features/effort_estimation/block_transformers.py index d315aefd065c..c3e6bce1f80e 100644 --- a/openedx/features/effort_estimation/block_transformers.py +++ b/openedx/features/effort_estimation/block_transformers.py @@ -53,7 +53,10 @@ class MissingEstimationData(Exception): @classmethod def name(cls): - """Unique identifier for the transformer's class; same identifier used in setup.py.""" + """ + Unique identifier for the transformer's class. + This must match the entry point name in the package configuration. + """ return 'effort_estimation' @classmethod diff --git a/openedx/tests/xblock_integration/test_external_xblocks.py b/openedx/tests/xblock_integration/test_external_xblocks.py index 01bdf2133cce..dbeba088fa47 100644 --- a/openedx/tests/xblock_integration/test_external_xblocks.py +++ b/openedx/tests/xblock_integration/test_external_xblocks.py @@ -29,9 +29,9 @@ class InvalidTestName(Exception): ''' This means you have an entry point for a test that does not correspond to a properly named test class. For example, if you cut-and-paste entry - points in `setup.py`, and forgot to repoint the class (so it points to - `DoneXBlock` instead of `TestDone`), or otherwise made an error, you - will see this exception. + points in your package configuration, and forgot to repoint the class + (so it points to `DoneXBlock` instead of `TestDone`), or otherwise made + an error, you will see this exception. ''' pass # lint-amnesty, pylint: disable=unnecessary-pass diff --git a/requirements/edx/github.in b/requirements/edx/github.in index 3c01759768c9..7fdb2c051ce8 100644 --- a/requirements/edx/github.in +++ b/requirements/edx/github.in @@ -57,19 +57,21 @@ # # * DIST-NAME is the distribution name, the same name you'd use in a # "pip install" command. It might be different than REPO-NAME. It must -# be the same as the `name="DIST-NAME"` value in the repo's setup.py. +# be the same as the package name defined in the repo's pyproject.toml +# (or setup.py for older packages). # # * VERSION might not be the same as TAG-OR-SHA, but if the tag names the # version, please make it match the VERSION, but with a "v" prefix. -# VERSION must be the same as the `version="VERSION"` value in the repo's -# setup.py. An alternative is to use 0.0 as VERSION: this forces pip to -# re-install the package each time, and can be useful when working with two -# repos before picking a version number. Don't use 0.0 on master, only for -# tight-loop work in progress. +# VERSION must be the same as the version defined in the repo's pyproject.toml +# (or setup.py for older packages). An alternative is to use 0.0 as VERSION: +# this forces pip to re-install the package each time, and can be useful when +# working with two repos before picking a version number. Don't use 0.0 on +# master, only for tight-loop work in progress. # # * Don't prefix the URL with "-e". That would install the package in "editable" -# mode A.K.A "development" mode, which takes longer and does not -# fully respect setup.py, making the transition back to PyPI more complex. +# mode A.K.A "development" mode, which takes longer and does not fully respect +# the package's build configuration, making the transition back to PyPI more +# complex. # # * Organize the URL into one of the two categories below: diff --git a/tox.ini b/tox.ini index e5df7f0fbd2f..c129fb8cf0d4 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ envlist = py{311} quality # This is needed to prevent the lms, cms, and openedx packages inside the "Open -# edX" package (defined in setup.py) from getting installed into site-packages +# edX" package (defined in pyproject.toml) from getting installed into site-packages # where they can get imported, which is bad because those won't even contain # most of the source code since we don't explicitly add anything to the source # distribution. diff --git a/xmodule/partitions/partitions.py b/xmodule/partitions/partitions.py index 9a9d637d318c..1ab152f81cf9 100644 --- a/xmodule/partitions/partitions.py +++ b/xmodule/partitions/partitions.py @@ -17,7 +17,7 @@ # * 1 -> 49: Unused/Reserved # * 50: The enrollment track partition # * 51: The content type gating partition (defined elsewhere) -# * 52-99: Available for other single user partitions, plugged in via setup.py. +# * 52-99: Available for other single user partitions, plugged in via entry points. # Operators, beware of conflicting IDs between plugins! # * 100 -> 2^31-1: General namespace for generating IDs at runtime. # This includes, at least: content partitions, the cohort partition, and teamset partitions.