@@ -75,6 +75,9 @@ def main() -> None:
7575 print ("Setting up the isort migration workflow..." )
7676 migrate_isort_workflow_setup ()
7777 print ("=" * 72 )
78+ print ("Excluding submodules from isort for API projects..." )
79+ migrate_isort_skip_glob_submodules ()
80+ print ("=" * 72 )
7881 print ()
7982
8083 if _manual_steps :
@@ -1393,6 +1396,103 @@ def read_cookiecutter_str_var(name: str) -> str | None:
13931396 return value
13941397
13951398
1399+ def migrate_isort_skip_glob_submodules () -> None :
1400+ """Exclude the ``submodules/`` directory from isort for API projects.
1401+
1402+ API repositories may embed external git submodules under ``submodules/``
1403+ that don't follow our import sorting rules. Without an explicit exclusion,
1404+ the automatic isort migration workflow descends into them when running
1405+ ``isort .``. The template now sets ``skip_glob = ["submodules/*"]`` under
1406+ ``[tool.isort]`` for API projects; this step mirrors that change in
1407+ existing repositories.
1408+
1409+ The function is a no-op for non-API projects, when ``pyproject.toml``
1410+ does not exist, or when the option already excludes ``submodules/``.
1411+ """
1412+ project_type = read_cookiecutter_str_var ("type" )
1413+ if project_type != "api" :
1414+ print (
1415+ " Skipped: not an API project "
1416+ f"(type={ project_type !r} ); only API repositories ship a "
1417+ "submodules/ directory."
1418+ )
1419+ return
1420+
1421+ pyproject = Path ("pyproject.toml" )
1422+ if not pyproject .exists ():
1423+ manual_step (
1424+ f"{ pyproject } not found; please add "
1425+ '`skip_glob = ["submodules/*"]` under `[tool.isort]` manually.'
1426+ )
1427+ return
1428+
1429+ try :
1430+ content = pyproject .read_text (encoding = "utf-8" )
1431+ except OSError as exc :
1432+ manual_step (
1433+ f"Failed to read { pyproject } : { exc } . Please add "
1434+ '`skip_glob = ["submodules/*"]` under `[tool.isort]` manually.'
1435+ )
1436+ return
1437+
1438+ isort_section_match = re .search (
1439+ r"(?ms)^\[tool\.isort\]\n.*?(?=^\[|\Z)" ,
1440+ content ,
1441+ )
1442+ if isort_section_match is None :
1443+ manual_step (
1444+ f"{ pyproject } does not contain a [tool.isort] section; please add "
1445+ '`skip_glob = ["submodules/*"]` manually.'
1446+ )
1447+ return
1448+
1449+ isort_section = isort_section_match .group (0 )
1450+ if '"submodules/*"' in isort_section or "'submodules/*'" in isort_section :
1451+ print (f" Skipped { pyproject } : already skips submodules/ in isort" )
1452+ return
1453+
1454+ if re .search (r"^skip_glob\s*=" , isort_section , flags = re .MULTILINE ):
1455+ manual_step (
1456+ f"{ pyproject } already contains a [tool.isort] skip_glob option; "
1457+ 'please add `"submodules/*"` to it manually.'
1458+ )
1459+ return
1460+
1461+ new_line = (
1462+ "# Submodules may contain external code that doesn't follow our import "
1463+ 'sorting rules.\n skip_glob = ["submodules/*"]\n '
1464+ )
1465+
1466+ # Anchor on the canonical [tool.isort] section as shipped by the template.
1467+ # Heavily customized layouts fall back to a manual step.
1468+ anchor_re = re .compile (
1469+ r"\[tool\.isort\]\n"
1470+ r'profile = "black"\n'
1471+ r"line_length = 88\n"
1472+ r"src_paths = \[[^\n]*\]\n" ,
1473+ )
1474+ match = anchor_re .search (content )
1475+ if match is None :
1476+ manual_step (
1477+ f"{ pyproject } does not match the expected [tool.isort] layout; "
1478+ 'please add `skip_glob = ["submodules/*"]` under `[tool.isort]` '
1479+ "manually."
1480+ )
1481+ return
1482+
1483+ anchor = match .group (0 )
1484+ new_content = content .replace (anchor , anchor + new_line , 1 )
1485+
1486+ try :
1487+ replace_file_atomically (pyproject , new_content )
1488+ print (f" Updated { pyproject } : added isort skip_glob for submodules/" )
1489+ except OSError as exc :
1490+ manual_step (
1491+ f"Failed to update { pyproject } : { exc } . Please add "
1492+ '`skip_glob = ["submodules/*"]` under `[tool.isort]` manually.'
1493+ )
1494+
1495+
13961496def migrate_black_extend_exclude_submodules () -> None :
13971497 """Exclude the ``submodules/`` directory from black for API projects.
13981498
0 commit comments