openrewrite-migrate-python 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. openrewrite_migrate_python/__init__.py +35 -0
  2. openrewrite_migrate_python/migrate/__init__.py +219 -0
  3. openrewrite_migrate_python/migrate/aifc_migrations.py +517 -0
  4. openrewrite_migrate_python/migrate/array_deprecations.py +133 -0
  5. openrewrite_migrate_python/migrate/ast_deprecations.py +322 -0
  6. openrewrite_migrate_python/migrate/asyncio_coroutine_to_async.py +225 -0
  7. openrewrite_migrate_python/migrate/asyncio_deprecations.py +117 -0
  8. openrewrite_migrate_python/migrate/calendar_deprecations.py +134 -0
  9. openrewrite_migrate_python/migrate/cgi_migrations.py +213 -0
  10. openrewrite_migrate_python/migrate/cgi_parse_deprecations.py +170 -0
  11. openrewrite_migrate_python/migrate/collections_abc_migrations.py +207 -0
  12. openrewrite_migrate_python/migrate/configparser_deprecations.py +252 -0
  13. openrewrite_migrate_python/migrate/datetime_utc.py +136 -0
  14. openrewrite_migrate_python/migrate/distutils_deprecations.py +107 -0
  15. openrewrite_migrate_python/migrate/distutils_migrations.py +125 -0
  16. openrewrite_migrate_python/migrate/functools_deprecations.py +98 -0
  17. openrewrite_migrate_python/migrate/gettext_deprecations.py +139 -0
  18. openrewrite_migrate_python/migrate/html_parser_deprecations.py +110 -0
  19. openrewrite_migrate_python/migrate/imp_migrations.py +135 -0
  20. openrewrite_migrate_python/migrate/langchain_classic_imports.py +308 -0
  21. openrewrite_migrate_python/migrate/langchain_community_imports.py +154 -0
  22. openrewrite_migrate_python/migrate/langchain_provider_imports.py +240 -0
  23. openrewrite_migrate_python/migrate/locale_deprecations.py +177 -0
  24. openrewrite_migrate_python/migrate/locale_getdefaultlocale_deprecation.py +103 -0
  25. openrewrite_migrate_python/migrate/macpath_deprecations.py +125 -0
  26. openrewrite_migrate_python/migrate/mailcap_migrations.py +129 -0
  27. openrewrite_migrate_python/migrate/nntplib_migrations.py +127 -0
  28. openrewrite_migrate_python/migrate/os_deprecations.py +158 -0
  29. openrewrite_migrate_python/migrate/pathlib_deprecations.py +98 -0
  30. openrewrite_migrate_python/migrate/pep594_system_migrations.py +429 -0
  31. openrewrite_migrate_python/migrate/pipes_migrations.py +130 -0
  32. openrewrite_migrate_python/migrate/pkgutil_deprecations.py +180 -0
  33. openrewrite_migrate_python/migrate/platform_deprecations.py +99 -0
  34. openrewrite_migrate_python/migrate/re_deprecations.py +122 -0
  35. openrewrite_migrate_python/migrate/removed_modules_312.py +139 -0
  36. openrewrite_migrate_python/migrate/shutil_deprecations.py +112 -0
  37. openrewrite_migrate_python/migrate/socket_deprecations.py +96 -0
  38. openrewrite_migrate_python/migrate/ssl_deprecations.py +121 -0
  39. openrewrite_migrate_python/migrate/string_formatting.py +526 -0
  40. openrewrite_migrate_python/migrate/sys_deprecations.py +97 -0
  41. openrewrite_migrate_python/migrate/sys_last_deprecations.py +104 -0
  42. openrewrite_migrate_python/migrate/tarfile_deprecations.py +120 -0
  43. openrewrite_migrate_python/migrate/telnetlib_migrations.py +132 -0
  44. openrewrite_migrate_python/migrate/tempfile_deprecations.py +113 -0
  45. openrewrite_migrate_python/migrate/threading_deprecations.py +488 -0
  46. openrewrite_migrate_python/migrate/threading_is_alive_deprecation.py +82 -0
  47. openrewrite_migrate_python/migrate/typing_callable.py +153 -0
  48. openrewrite_migrate_python/migrate/typing_deprecations.py +337 -0
  49. openrewrite_migrate_python/migrate/typing_union_to_pipe.py +272 -0
  50. openrewrite_migrate_python/migrate/unittest_deprecations.py +140 -0
  51. openrewrite_migrate_python/migrate/upgrade_to_langchain02.py +56 -0
  52. openrewrite_migrate_python/migrate/upgrade_to_langchain1.py +66 -0
  53. openrewrite_migrate_python/migrate/upgrade_to_python310.py +91 -0
  54. openrewrite_migrate_python/migrate/upgrade_to_python311.py +92 -0
  55. openrewrite_migrate_python/migrate/upgrade_to_python312.py +101 -0
  56. openrewrite_migrate_python/migrate/upgrade_to_python313.py +109 -0
  57. openrewrite_migrate_python/migrate/upgrade_to_python314.py +87 -0
  58. openrewrite_migrate_python/migrate/upgrade_to_python38.py +85 -0
  59. openrewrite_migrate_python/migrate/upgrade_to_python39.py +125 -0
  60. openrewrite_migrate_python/migrate/urllib_deprecations.py +204 -0
  61. openrewrite_migrate_python/migrate/uu_migrations.py +129 -0
  62. openrewrite_migrate_python/migrate/xdrlib_migrations.py +129 -0
  63. openrewrite_migrate_python/migrate/xml_deprecations.py +154 -0
  64. openrewrite_migrate_python-0.1.0.dist-info/METADATA +31 -0
  65. openrewrite_migrate_python-0.1.0.dist-info/RECORD +68 -0
  66. openrewrite_migrate_python-0.1.0.dist-info/WHEEL +5 -0
  67. openrewrite_migrate_python-0.1.0.dist-info/entry_points.txt +2 -0
  68. openrewrite_migrate_python-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,92 @@
1
+ """Composite recipe for upgrading from Python 3.10 to Python 3.11."""
2
+
3
+ from typing import List
4
+
5
+ from rewrite import Recipe
6
+ from rewrite.category import CategoryDescriptor
7
+ from rewrite.decorators import categorize
8
+ from rewrite.marketplace import Python
9
+ from rewrite.python.recipes import ChangeImport
10
+
11
+ # Define category path: Python > Migrate
12
+ _Migrate = [*Python, CategoryDescriptor(display_name="Migrate")]
13
+
14
+
15
+ @categorize(_Migrate)
16
+ class UpgradeToPython311(Recipe):
17
+ """
18
+ Migrate deprecated and removed APIs for Python 3.11 compatibility.
19
+
20
+ This composite recipe applies all necessary migrations for code
21
+ running on Python 3.10 to be compatible with Python 3.11.
22
+
23
+ Key changes in Python 3.11:
24
+ - The `@asyncio.coroutine` decorator is removed (use `async def` instead)
25
+ - `inspect.getargspec()` is removed (use `inspect.getfullargspec()`)
26
+ - `gettext.lgettext()` and related functions removed
27
+
28
+ See: https://docs.python.org/3/whatsnew/3.11.html
29
+ """
30
+
31
+ @property
32
+ def name(self) -> str:
33
+ return "org.openrewrite.python.migrate.UpgradeToPython311"
34
+
35
+ @property
36
+ def display_name(self) -> str:
37
+ return "Upgrade to Python 3.11"
38
+
39
+ @property
40
+ def description(self) -> str:
41
+ return (
42
+ "Migrate deprecated and removed APIs for Python 3.11 compatibility. "
43
+ "This includes handling removed modules, deprecated functions, and "
44
+ "API changes between Python 3.10 and 3.11."
45
+ )
46
+
47
+ @property
48
+ def tags(self) -> List[str]:
49
+ return ["python", "migration", "3.11"]
50
+
51
+ def recipe_list(self) -> List[Recipe]:
52
+ """Return the list of recipes to apply for Python 3.11 upgrade."""
53
+ # Import here to avoid circular imports
54
+ from .asyncio_coroutine_to_async import MigrateAsyncioCoroutine
55
+ from .asyncio_deprecations import FindAsyncioCoroutineDecorator
56
+ from .locale_getdefaultlocale_deprecation import FindLocaleGetdefaultlocale
57
+ from .re_deprecations import FindReTemplate
58
+ from .configparser_deprecations import (
59
+ ReplaceConfigparserReadfp,
60
+ ReplaceConfigparserSafeConfigParser,
61
+ )
62
+ from .gettext_deprecations import ReplaceGettextDeprecations
63
+ from .socket_deprecations import FindSocketGetFQDN
64
+ from .unittest_deprecations import ReplaceUnittestDeprecatedAliases
65
+ from .upgrade_to_python310 import UpgradeToPython310
66
+
67
+ return [
68
+ # First apply all Python 3.10 upgrades
69
+ UpgradeToPython310(),
70
+ # inspect.getargspec() was deprecated in 3.0 and removed in 3.11
71
+ ChangeImport(old_module="inspect", old_name="getargspec", new_module="inspect", new_name="getfullargspec"),
72
+ # configparser.SafeConfigParser deprecated in 3.2, removed in 3.12
73
+ ReplaceConfigparserSafeConfigParser(),
74
+ # configparser.readfp() deprecated in 3.2, removed in 3.13
75
+ ReplaceConfigparserReadfp(),
76
+ # gettext.lgettext() etc. deprecated in 3.8, removed in 3.11
77
+ ReplaceGettextDeprecations(),
78
+ # logging.warn() deprecated in 3.3 (still works but not recommended)
79
+ ChangeImport(old_module="logging", old_name="warn", new_module="logging", new_name="warning"),
80
+ # unittest method aliases (assertEquals, etc.) removed in 3.11/3.12
81
+ ReplaceUnittestDeprecatedAliases(),
82
+ # @asyncio.coroutine + yield from -> async def + await (removed in 3.11)
83
+ MigrateAsyncioCoroutine(),
84
+ # Detect @asyncio.coroutine usage that can't be auto-migrated
85
+ FindAsyncioCoroutineDecorator(),
86
+ # socket.getfqdn() deprecated in 3.11
87
+ FindSocketGetFQDN(),
88
+ # locale.getdefaultlocale() deprecated in 3.11
89
+ FindLocaleGetdefaultlocale(),
90
+ # re.template() / re.TEMPLATE deprecated in 3.11
91
+ FindReTemplate(),
92
+ ]
@@ -0,0 +1,101 @@
1
+ """Composite recipe for upgrading from Python 3.11 to Python 3.12."""
2
+
3
+ from typing import List
4
+
5
+ from rewrite import Recipe
6
+ from rewrite.category import CategoryDescriptor
7
+ from rewrite.decorators import categorize
8
+ from rewrite.marketplace import Python
9
+ from rewrite.python.recipes import ChangeImport
10
+
11
+ # Define category path: Python > Migrate
12
+ _Migrate = [*Python, CategoryDescriptor(display_name="Migrate")]
13
+
14
+
15
+ @categorize(_Migrate)
16
+ class UpgradeToPython312(Recipe):
17
+ """
18
+ Migrate deprecated and removed APIs for Python 3.12 compatibility.
19
+
20
+ This composite recipe applies all necessary migrations for code
21
+ running on Python 3.11 to be compatible with Python 3.12.
22
+
23
+ Key changes in Python 3.12:
24
+ - `datetime.utcnow()` and `utcfromtimestamp()` deprecated
25
+ - The `imp` module is removed (use `importlib` instead)
26
+ - `asynchat`, `asyncore`, and `smtpd` modules removed
27
+ - `distutils` is fully removed (use `setuptools` or `packaging`)
28
+ - `configparser.SafeConfigParser` is removed (use `ConfigParser`)
29
+ - `Path.link_to()` removed (use `hardlink_to()` with reversed args)
30
+
31
+ See: https://docs.python.org/3/whatsnew/3.12.html
32
+ """
33
+
34
+ @property
35
+ def name(self) -> str:
36
+ return "org.openrewrite.python.migrate.UpgradeToPython312"
37
+
38
+ @property
39
+ def display_name(self) -> str:
40
+ return "Upgrade to Python 3.12"
41
+
42
+ @property
43
+ def description(self) -> str:
44
+ return (
45
+ "Migrate deprecated and removed APIs for Python 3.12 compatibility. "
46
+ "This includes detecting usage of the removed `imp` module and other "
47
+ "legacy modules that were removed in Python 3.12."
48
+ )
49
+
50
+ @property
51
+ def tags(self) -> List[str]:
52
+ return ["python", "migration", "3.12"]
53
+
54
+ def recipe_list(self) -> List[Recipe]:
55
+ """Return the list of recipes to apply for Python 3.12 upgrade."""
56
+ # Import here to avoid circular imports
57
+ from .calendar_deprecations import ReplaceCalendarConstants
58
+ from .datetime_utc import ReplaceDatetimeUtcFromTimestamp, ReplaceDatetimeUtcNow
59
+ from .shutil_deprecations import FindShutilRmtreeOnerror
60
+ from .sys_last_deprecations import FindSysLastExcInfo
61
+ from .distutils_deprecations import FindDistutilsUsage
62
+ from .distutils_migrations import ReplaceDistutilsVersion
63
+ from .imp_migrations import FindImpUsage
64
+ from .os_deprecations import FindOsPopen, FindOsSpawn
65
+ from .pathlib_deprecations import FindPathlibLinkTo
66
+ from .pkgutil_deprecations import ReplacePkgutilFindLoader, ReplacePkgutilGetLoader
67
+ from .removed_modules_312 import FindRemovedModules312
68
+ from .ssl_deprecations import FindSslMatchHostname
69
+ from .upgrade_to_python311 import UpgradeToPython311
70
+
71
+ return [
72
+ # First apply all Python 3.11 upgrades (which includes 3.10 upgrades)
73
+ UpgradeToPython311(),
74
+ # datetime.utcnow() / utcfromtimestamp() deprecated in 3.12
75
+ ReplaceDatetimeUtcNow(),
76
+ ReplaceDatetimeUtcFromTimestamp(),
77
+ # calendar mixed-case constants deprecated in Python 3.12
78
+ ReplaceCalendarConstants(),
79
+ # imp module was removed in Python 3.12 - auto-fix where possible
80
+ ChangeImport(old_module="imp", old_name="reload", new_module="importlib"), # imp.reload() -> importlib.reload()
81
+ FindImpUsage(), # Flag any remaining imp usage
82
+ # distutils removed in 3.12 (detection only)
83
+ ReplaceDistutilsVersion(),
84
+ FindDistutilsUsage(),
85
+ # pkgutil.find_loader/get_loader deprecated in 3.12 (detection only)
86
+ ReplacePkgutilFindLoader(),
87
+ ReplacePkgutilGetLoader(),
88
+ # Path.link_to() removed in 3.12
89
+ FindPathlibLinkTo(),
90
+ # Detect imports of modules removed in 3.12
91
+ FindRemovedModules312(),
92
+ # ssl.match_hostname() removed in 3.12
93
+ FindSslMatchHostname(),
94
+ # os.popen/os.spawn* deprecated (use subprocess)
95
+ FindOsPopen(),
96
+ FindOsSpawn(),
97
+ # shutil.rmtree(onerror=) deprecated in 3.12
98
+ FindShutilRmtreeOnerror(),
99
+ # sys.last_type/last_value/last_traceback deprecated in 3.12
100
+ FindSysLastExcInfo(),
101
+ ]
@@ -0,0 +1,109 @@
1
+ """Composite recipe for upgrading from Python 3.12 to Python 3.13."""
2
+
3
+ from typing import List
4
+
5
+ from rewrite import Recipe
6
+ from rewrite.category import CategoryDescriptor
7
+ from rewrite.decorators import categorize
8
+ from rewrite.marketplace import Python
9
+
10
+ # Define category path: Python > Migrate
11
+ _Migrate = [*Python, CategoryDescriptor(display_name="Migrate")]
12
+
13
+
14
+ @categorize(_Migrate)
15
+ class UpgradeToPython313(Recipe):
16
+ """
17
+ Migrate deprecated and removed APIs for Python 3.13 compatibility.
18
+
19
+ This composite recipe applies all necessary migrations for code
20
+ running on Python 3.12 to be compatible with Python 3.13.
21
+
22
+ Key changes in Python 3.13 (PEP 594 "dead batteries" removals):
23
+ - `cgi` and `cgitb` modules removed
24
+ - `telnetlib` module removed
25
+ - `nntplib` module removed
26
+ - `pipes` module removed
27
+ - `uu` module removed
28
+ - Many other legacy modules removed (aifc, audioop, chunk, crypt,
29
+ imghdr, mailcap, msilib, nis, ossaudiodev, sndhdr, spwd, sunau, xdrlib)
30
+ - `configparser.readfp()` removed (deprecated since 3.2)
31
+
32
+ See: https://docs.python.org/3/whatsnew/3.13.html
33
+ See: https://peps.python.org/pep-0594/
34
+ """
35
+
36
+ @property
37
+ def name(self) -> str:
38
+ return "org.openrewrite.python.migrate.UpgradeToPython313"
39
+
40
+ @property
41
+ def display_name(self) -> str:
42
+ return "Upgrade to Python 3.13"
43
+
44
+ @property
45
+ def description(self) -> str:
46
+ return (
47
+ "Migrate deprecated and removed APIs for Python 3.13 compatibility. "
48
+ "This includes detecting usage of modules removed in PEP 594 "
49
+ "('dead batteries') and other API changes between Python 3.12 and 3.13."
50
+ )
51
+
52
+ @property
53
+ def tags(self) -> List[str]:
54
+ return ["python", "migration", "3.13"]
55
+
56
+ def recipe_list(self) -> List[Recipe]:
57
+ """Return the list of recipes to apply for Python 3.13 upgrade."""
58
+ # Import here to avoid circular imports
59
+ from .aifc_migrations import (
60
+ FindAifcModule,
61
+ FindAudioopModule,
62
+ FindChunkModule,
63
+ FindImghdrModule,
64
+ FindSndhdrModule,
65
+ FindSunauModule,
66
+ )
67
+ from .cgi_migrations import FindCgiModule, FindCgitbModule
68
+ from .locale_deprecations import ReplaceLocaleResetlocale
69
+ from .mailcap_migrations import FindMailcapModule
70
+ from .nntplib_migrations import FindNntplibModule
71
+ from .pep594_system_migrations import (
72
+ FindCryptModule,
73
+ FindMsilibModule,
74
+ FindNisModule,
75
+ FindOssaudiodevModule,
76
+ FindSpwdModule,
77
+ )
78
+ from .pipes_migrations import FindPipesModule
79
+ from .telnetlib_migrations import FindTelnetlibModule
80
+ from .upgrade_to_python312 import UpgradeToPython312
81
+ from .uu_migrations import FindUuModule
82
+ from .xdrlib_migrations import FindXdrlibModule
83
+
84
+ return [
85
+ # First apply all Python 3.12 upgrades (which includes 3.11 and 3.10)
86
+ UpgradeToPython312(),
87
+ # locale.resetlocale() removed in 3.13 (deprecated in 3.11)
88
+ ReplaceLocaleResetlocale(),
89
+ # PEP 594 "dead batteries" - removed modules (detection only)
90
+ FindAifcModule(),
91
+ FindAudioopModule(),
92
+ FindCgiModule(),
93
+ FindCgitbModule(),
94
+ FindChunkModule(),
95
+ FindCryptModule(),
96
+ FindImghdrModule(),
97
+ FindMailcapModule(),
98
+ FindMsilibModule(),
99
+ FindNisModule(),
100
+ FindNntplibModule(),
101
+ FindOssaudiodevModule(),
102
+ FindPipesModule(),
103
+ FindSndhdrModule(),
104
+ FindSpwdModule(),
105
+ FindSunauModule(),
106
+ FindTelnetlibModule(),
107
+ FindUuModule(),
108
+ FindXdrlibModule(),
109
+ ]
@@ -0,0 +1,87 @@
1
+ """Composite recipe for upgrading from Python 3.13 to Python 3.14."""
2
+
3
+ from typing import List
4
+
5
+ from rewrite import Recipe
6
+ from rewrite.category import CategoryDescriptor
7
+ from rewrite.decorators import categorize
8
+ from rewrite.marketplace import Python
9
+
10
+ # Define category path: Python > Migrate
11
+ _Migrate = [*Python, CategoryDescriptor(display_name="Migrate")]
12
+
13
+
14
+ @categorize(_Migrate)
15
+ class UpgradeToPython314(Recipe):
16
+ """
17
+ Migrate deprecated and removed APIs for Python 3.14 compatibility.
18
+
19
+ This composite recipe applies all necessary migrations for code
20
+ running on Python 3.13 to be compatible with Python 3.14.
21
+
22
+ Key changes in Python 3.14:
23
+ - `ast.Num`, `ast.Str`, `ast.Bytes`, `ast.NameConstant`, `ast.Ellipsis`
24
+ removed (deprecated since 3.8, use `ast.Constant` instead)
25
+ - `array.tostring()` / `fromstring()` removed (use tobytes/frombytes)
26
+ - `tempfile.mktemp()` removed (security issue, use mkstemp)
27
+ - Various urllib.parse split functions removed
28
+
29
+ See: https://docs.python.org/3/whatsnew/3.14.html
30
+ """
31
+
32
+ @property
33
+ def name(self) -> str:
34
+ return "org.openrewrite.python.migrate.UpgradeToPython314"
35
+
36
+ @property
37
+ def display_name(self) -> str:
38
+ return "Upgrade to Python 3.14"
39
+
40
+ @property
41
+ def description(self) -> str:
42
+ return (
43
+ "Migrate deprecated and removed APIs for Python 3.14 compatibility. "
44
+ "This includes replacing deprecated AST node types with `ast.Constant` "
45
+ "and other API changes between Python 3.13 and 3.14."
46
+ )
47
+
48
+ @property
49
+ def tags(self) -> List[str]:
50
+ return ["python", "migration", "3.14"]
51
+
52
+ def recipe_list(self) -> List[Recipe]:
53
+ """Return the list of recipes to apply for Python 3.14 upgrade."""
54
+ # Import here to avoid circular imports
55
+ from .array_deprecations import ReplaceArrayFromstring, ReplaceArrayTostring
56
+ from .ast_deprecations import (
57
+ ReplaceAstBytes,
58
+ ReplaceAstEllipsis,
59
+ ReplaceAstNameConstant,
60
+ ReplaceAstNum,
61
+ ReplaceAstStr,
62
+ )
63
+ from .tempfile_deprecations import FindTempfileMktemp
64
+ from .upgrade_to_python313 import UpgradeToPython313
65
+ from .urllib_deprecations import (
66
+ FindUrllibParseSplitFunctions,
67
+ FindUrllibParseToBytes,
68
+ )
69
+
70
+ return [
71
+ # First apply all Python 3.13 upgrades (which includes 3.12, 3.11, 3.10)
72
+ UpgradeToPython313(),
73
+ # Array methods removed in 3.14 (deprecated since 3.2)
74
+ ReplaceArrayTostring(), # tostring() -> tobytes()
75
+ ReplaceArrayFromstring(), # fromstring() -> frombytes()
76
+ # ast.Num, ast.Str, etc. removed in 3.14 (deprecated since 3.8)
77
+ ReplaceAstNum(),
78
+ ReplaceAstStr(),
79
+ ReplaceAstBytes(),
80
+ ReplaceAstNameConstant(),
81
+ ReplaceAstEllipsis(),
82
+ # tempfile.mktemp() removed (security issue)
83
+ FindTempfileMktemp(),
84
+ # urllib.parse split functions removed
85
+ FindUrllibParseSplitFunctions(),
86
+ FindUrllibParseToBytes(),
87
+ ]
@@ -0,0 +1,85 @@
1
+ """Composite recipe for upgrading to Python 3.8."""
2
+
3
+ from typing import List
4
+
5
+ from rewrite import Recipe
6
+ from rewrite.category import CategoryDescriptor
7
+ from rewrite.decorators import categorize
8
+ from rewrite.marketplace import Python
9
+ from rewrite.python.recipes import ChangeImport
10
+
11
+ # Define category path: Python > Migrate
12
+ _Migrate = [*Python, CategoryDescriptor(display_name="Migrate")]
13
+
14
+
15
+ @categorize(_Migrate)
16
+ class UpgradeToPython38(Recipe):
17
+ """
18
+ Migrate deprecated APIs and detect legacy patterns for Python 3.8 compatibility.
19
+
20
+ This composite recipe applies migrations for code targeting Python 3.8.
21
+
22
+ Key changes:
23
+ - Replace `time.clock()` with `time.perf_counter()`
24
+ - Replace `cgi.escape()` with `html.escape()`
25
+ - Detect removed `cgi.parse_qs()` / `cgi.parse_qsl()` usage
26
+ - Detect removed `platform.popen()` usage
27
+ - Detect removed `macpath` module usage
28
+ - Detect removed `tarfile.filemode` usage
29
+ - Detect removed `sys.set_coroutine_wrapper()` / `sys.get_coroutine_wrapper()`
30
+ - Detect legacy string formatting (% formatting, str.format())
31
+ - Detect deprecated `functools.cmp_to_key()` usage
32
+
33
+ See: https://docs.python.org/3/whatsnew/3.8.html
34
+ """
35
+
36
+ @property
37
+ def name(self) -> str:
38
+ return "org.openrewrite.python.migrate.UpgradeToPython38"
39
+
40
+ @property
41
+ def display_name(self) -> str:
42
+ return "Upgrade to Python 3.8"
43
+
44
+ @property
45
+ def description(self) -> str:
46
+ return (
47
+ "Migrate deprecated APIs and detect legacy patterns for Python 3.8 compatibility."
48
+ )
49
+
50
+ @property
51
+ def tags(self) -> List[str]:
52
+ return ["python", "migration", "3.8"]
53
+
54
+ def recipe_list(self) -> List[Recipe]:
55
+ """Return the list of recipes to apply for Python 3.8 upgrade."""
56
+ from .cgi_parse_deprecations import FindCgiParseQs, FindCgiParseQsl
57
+ from .functools_deprecations import FindFunctoolsCmpToKey
58
+ from .macpath_deprecations import FindMacpathModule
59
+ from .platform_deprecations import FindPlatformPopen
60
+ from .string_formatting import ReplacePercentFormatWithFString, ReplaceStrFormatWithFString
61
+ from .sys_deprecations import FindSysCoroutineWrapper
62
+ from .tarfile_deprecations import FindTarfileFilemode
63
+ return [
64
+ # Auto-fix: time.clock() -> time.perf_counter()
65
+ ChangeImport(old_module="time", old_name="clock", new_module="time", new_name="perf_counter"),
66
+ # Auto-fix: cgi.escape() -> html.escape()
67
+ ChangeImport(old_module="cgi", old_name="escape", new_module="html"),
68
+ # Detect removed cgi.parse_qs() / cgi.parse_qsl()
69
+ FindCgiParseQs(),
70
+ FindCgiParseQsl(),
71
+ # Detect removed platform.popen()
72
+ FindPlatformPopen(),
73
+ # Detect removed macpath module
74
+ FindMacpathModule(),
75
+ # Detect removed tarfile.filemode
76
+ FindTarfileFilemode(),
77
+ # Detect removed sys.set_coroutine_wrapper()/get_coroutine_wrapper()
78
+ FindSysCoroutineWrapper(),
79
+
80
+ # String formatting migrations
81
+ ReplaceStrFormatWithFString(),
82
+ ReplacePercentFormatWithFString(),
83
+ # functools.cmp_to_key deprecated
84
+ FindFunctoolsCmpToKey(),
85
+ ]
@@ -0,0 +1,125 @@
1
+ """Composite recipe for upgrading from Python 3.8 to Python 3.9."""
2
+
3
+ from typing import List
4
+
5
+ from rewrite import Recipe
6
+ from rewrite.category import CategoryDescriptor
7
+ from rewrite.decorators import categorize
8
+ from rewrite.marketplace import Python
9
+ from rewrite.python.recipes import ChangeImport
10
+
11
+ # Define category path: Python > Migrate
12
+ _Migrate = [*Python, CategoryDescriptor(display_name="Migrate")]
13
+
14
+
15
+ @categorize(_Migrate)
16
+ class UpgradeToPython39(Recipe):
17
+ """
18
+ Migrate deprecated APIs for Python 3.9 compatibility.
19
+
20
+ This composite recipe applies all necessary migrations for code
21
+ running on Python 3.8 to be compatible with Python 3.9.
22
+
23
+ Key changes in Python 3.9:
24
+ - PEP 585: Built-in generics (`list[int]` instead of `typing.List[int]`)
25
+ - `base64.encodestring()` / `decodestring()` removed
26
+ - `Element.getiterator()` deprecated (use `iter()`)
27
+ - `Element.getchildren()` deprecated (use `list(element)`)
28
+ - `typing.Callable` deprecated in favor of `collections.abc.Callable`
29
+
30
+ See: https://docs.python.org/3/whatsnew/3.9.html
31
+ """
32
+
33
+ @property
34
+ def name(self) -> str:
35
+ return "org.openrewrite.python.migrate.UpgradeToPython39"
36
+
37
+ @property
38
+ def display_name(self) -> str:
39
+ return "Upgrade to Python 3.9"
40
+
41
+ @property
42
+ def description(self) -> str:
43
+ return (
44
+ "Migrate deprecated APIs for Python 3.9 compatibility. "
45
+ "This includes PEP 585 built-in generics, removed base64 functions, "
46
+ "and deprecated XML Element methods."
47
+ )
48
+
49
+ @property
50
+ def tags(self) -> List[str]:
51
+ return ["python", "migration", "3.9"]
52
+
53
+ def recipe_list(self) -> List[Recipe]:
54
+ """Return the list of recipes to apply for Python 3.9 upgrade."""
55
+ from .html_parser_deprecations import FindHtmlParserUnescape
56
+ from .threading_is_alive_deprecation import ReplaceThreadIsAlive
57
+ from .typing_callable import ReplaceTypingCallableWithCollectionsAbcCallable
58
+ from .upgrade_to_python38 import UpgradeToPython38
59
+ from .xml_deprecations import FindElementGetchildren, ReplaceElementGetiterator
60
+
61
+ return [
62
+ # First apply all Python 3.8 upgrades
63
+ UpgradeToPython38(),
64
+ # base64 encodestring/decodestring removed in Python 3.9
65
+ ChangeImport(old_module="base64", old_name="encodestring", new_module="base64", new_name="encodebytes"),
66
+ ChangeImport(old_module="base64", old_name="decodestring", new_module="base64", new_name="decodebytes"),
67
+ # PEP 585: Replace typing.List[X] with list[X], etc. (Python 3.9+)
68
+ ChangeImport(old_module="typing", old_name="List", new_module="builtins", new_name="list"),
69
+ ChangeImport(old_module="typing", old_name="Dict", new_module="builtins", new_name="dict"),
70
+ ChangeImport(old_module="typing", old_name="Set", new_module="builtins", new_name="set"),
71
+ ChangeImport(old_module="typing", old_name="FrozenSet", new_module="builtins", new_name="frozenset"),
72
+ ChangeImport(old_module="typing", old_name="Tuple", new_module="builtins", new_name="tuple"),
73
+ ChangeImport(old_module="typing", old_name="Type", new_module="builtins", new_name="type"),
74
+ # typing.Callable -> collections.abc.Callable (PEP 585)
75
+ ReplaceTypingCallableWithCollectionsAbcCallable(),
76
+ # PEP 585: typing -> collections replacements
77
+ ChangeImport(old_module="typing", old_name="Deque", new_module="collections", new_name="deque"),
78
+ ChangeImport(old_module="typing", old_name="DefaultDict", new_module="collections", new_name="defaultdict"),
79
+ ChangeImport(old_module="typing", old_name="OrderedDict", new_module="collections"),
80
+ ChangeImport(old_module="typing", old_name="Counter", new_module="collections"),
81
+ ChangeImport(old_module="typing", old_name="ChainMap", new_module="collections"),
82
+ # PEP 585: typing -> re replacements
83
+ ChangeImport(old_module="typing", old_name="Pattern", new_module="re"),
84
+ ChangeImport(old_module="typing", old_name="Match", new_module="re"),
85
+ # PEP 585: typing -> contextlib replacements
86
+ ChangeImport(old_module="typing", old_name="ContextManager", new_module="contextlib", new_name="AbstractContextManager"),
87
+ ChangeImport(old_module="typing", old_name="AsyncContextManager", new_module="contextlib", new_name="AbstractAsyncContextManager"),
88
+ # PEP 585: typing -> collections.abc replacements
89
+ ChangeImport(old_module="typing", old_name="Iterable", new_module="collections.abc"),
90
+ ChangeImport(old_module="typing", old_name="Iterator", new_module="collections.abc"),
91
+ ChangeImport(old_module="typing", old_name="Generator", new_module="collections.abc"),
92
+ ChangeImport(old_module="typing", old_name="Sequence", new_module="collections.abc"),
93
+ ChangeImport(old_module="typing", old_name="MutableSequence", new_module="collections.abc"),
94
+ ChangeImport(old_module="typing", old_name="Mapping", new_module="collections.abc"),
95
+ ChangeImport(old_module="typing", old_name="MutableMapping", new_module="collections.abc"),
96
+ ChangeImport(old_module="typing", old_name="AbstractSet", new_module="collections.abc", new_name="Set"),
97
+ ChangeImport(old_module="typing", old_name="MutableSet", new_module="collections.abc"),
98
+ ChangeImport(old_module="typing", old_name="Awaitable", new_module="collections.abc"),
99
+ ChangeImport(old_module="typing", old_name="Coroutine", new_module="collections.abc"),
100
+ ChangeImport(old_module="typing", old_name="AsyncIterable", new_module="collections.abc"),
101
+ ChangeImport(old_module="typing", old_name="AsyncIterator", new_module="collections.abc"),
102
+ ChangeImport(old_module="typing", old_name="AsyncGenerator", new_module="collections.abc"),
103
+ ChangeImport(old_module="typing", old_name="Reversible", new_module="collections.abc"),
104
+ ChangeImport(old_module="typing", old_name="Container", new_module="collections.abc"),
105
+ ChangeImport(old_module="typing", old_name="Collection", new_module="collections.abc"),
106
+ ChangeImport(old_module="typing", old_name="MappingView", new_module="collections.abc"),
107
+ ChangeImport(old_module="typing", old_name="KeysView", new_module="collections.abc"),
108
+ ChangeImport(old_module="typing", old_name="ItemsView", new_module="collections.abc"),
109
+ ChangeImport(old_module="typing", old_name="ValuesView", new_module="collections.abc"),
110
+ ChangeImport(old_module="typing", old_name="Hashable", new_module="collections.abc"),
111
+ ChangeImport(old_module="typing", old_name="Sized", new_module="collections.abc"),
112
+ # XML Element.getiterator() deprecated in 3.9, use iter()
113
+ ReplaceElementGetiterator(),
114
+ # XML Element.getchildren() deprecated in 3.9 (detection only)
115
+ FindElementGetchildren(),
116
+ # fractions.gcd() removed in 3.9, use math.gcd()
117
+ ChangeImport(old_module="fractions", old_name="gcd", new_module="math"),
118
+ # sys.getcheckinterval/setcheckinterval removed in 3.9
119
+ ChangeImport(old_module="sys", old_name="getcheckinterval", new_module="sys", new_name="getswitchinterval"),
120
+ ChangeImport(old_module="sys", old_name="setcheckinterval", new_module="sys", new_name="setswitchinterval"),
121
+ # Thread.isAlive() removed in 3.9
122
+ ReplaceThreadIsAlive(),
123
+ # HTMLParser.unescape() removed in 3.9
124
+ FindHtmlParserUnescape(),
125
+ ]