wexample-filestate-python 0.0.48__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 (80) hide show
  1. wexample_filestate_python/__init__.py +0 -0
  2. wexample_filestate_python/__pycache__/__init__.py +0 -0
  3. wexample_filestate_python/common/__init__.py +0 -0
  4. wexample_filestate_python/common/__pycache__/__init__.py +0 -0
  5. wexample_filestate_python/common/pipy_gateway.py +20 -0
  6. wexample_filestate_python/config_option/__init__.py +0 -0
  7. wexample_filestate_python/config_option/__pycache__/__init__.py +0 -0
  8. wexample_filestate_python/config_option/mixin/__init__.py +0 -0
  9. wexample_filestate_python/config_option/mixin/__pycache__/__init__.py +0 -0
  10. wexample_filestate_python/config_option/mixin/with_stdout_wrapping_mixin.py +46 -0
  11. wexample_filestate_python/config_value/__init__.py +0 -0
  12. wexample_filestate_python/config_value/__pycache__/__init__.py +0 -0
  13. wexample_filestate_python/config_value/python_config_value.py +195 -0
  14. wexample_filestate_python/const/__init__.py +0 -0
  15. wexample_filestate_python/const/__pycache__/__init__.py +0 -0
  16. wexample_filestate_python/const/name_pattern.py +4 -0
  17. wexample_filestate_python/const/python_file.py +5 -0
  18. wexample_filestate_python/file/__init__.py +0 -0
  19. wexample_filestate_python/file/__pycache__/__init__.py +0 -0
  20. wexample_filestate_python/file/python_file.py +12 -0
  21. wexample_filestate_python/helpers/__init__.py +0 -0
  22. wexample_filestate_python/helpers/__pycache__/__init__.py +0 -0
  23. wexample_filestate_python/helpers/package.py +122 -0
  24. wexample_filestate_python/helpers/toml.py +116 -0
  25. wexample_filestate_python/option/__init__.py +0 -0
  26. wexample_filestate_python/option/__pycache__/__init__.py +0 -0
  27. wexample_filestate_python/option/abstract_python_file_content_option.py +45 -0
  28. wexample_filestate_python/option/add_future_annotations_option.py +79 -0
  29. wexample_filestate_python/option/add_return_types_option.py +265 -0
  30. wexample_filestate_python/option/fix_attrs_option.py +37 -0
  31. wexample_filestate_python/option/fix_blank_lines_option.py +47 -0
  32. wexample_filestate_python/option/format_option.py +34 -0
  33. wexample_filestate_python/option/fstringify_option.py +34 -0
  34. wexample_filestate_python/option/modernize_typing_option.py +25 -0
  35. wexample_filestate_python/option/order_class_attributes_option.py +34 -0
  36. wexample_filestate_python/option/order_class_docstring_option.py +36 -0
  37. wexample_filestate_python/option/order_class_methods_option.py +37 -0
  38. wexample_filestate_python/option/order_constants_option.py +35 -0
  39. wexample_filestate_python/option/order_iterable_items_option.py +31 -0
  40. wexample_filestate_python/option/order_main_guard_option.py +44 -0
  41. wexample_filestate_python/option/order_module_docstring_option.py +73 -0
  42. wexample_filestate_python/option/order_module_functions_option.py +42 -0
  43. wexample_filestate_python/option/order_module_metadata_option.py +62 -0
  44. wexample_filestate_python/option/order_type_checking_block_option.py +51 -0
  45. wexample_filestate_python/option/python_option.py +164 -0
  46. wexample_filestate_python/option/relocate_imports_option.py +189 -0
  47. wexample_filestate_python/option/remove_unused_option.py +45 -0
  48. wexample_filestate_python/option/sort_imports_option.py +26 -0
  49. wexample_filestate_python/option/unquote_annotations_option.py +85 -0
  50. wexample_filestate_python/options_provider/__init__.py +0 -0
  51. wexample_filestate_python/options_provider/__pycache__/__init__.py +0 -0
  52. wexample_filestate_python/options_provider/python_options_provider.py +24 -0
  53. wexample_filestate_python/py.typed +0 -0
  54. wexample_filestate_python/utils/__init__.py +0 -0
  55. wexample_filestate_python/utils/__pycache__/__init__.py +0 -0
  56. wexample_filestate_python/utils/python_attrs_utils.py +112 -0
  57. wexample_filestate_python/utils/python_blank_lines_utils.py +568 -0
  58. wexample_filestate_python/utils/python_class_attributes_utils.py +275 -0
  59. wexample_filestate_python/utils/python_class_docstring_utils.py +85 -0
  60. wexample_filestate_python/utils/python_class_methods_utils.py +230 -0
  61. wexample_filestate_python/utils/python_constants_utils.py +302 -0
  62. wexample_filestate_python/utils/python_docstring_utils.py +117 -0
  63. wexample_filestate_python/utils/python_functions_utils.py +212 -0
  64. wexample_filestate_python/utils/python_iterable_utils.py +131 -0
  65. wexample_filestate_python/utils/python_main_guard_utils.py +80 -0
  66. wexample_filestate_python/utils/python_module_metadata_utils.py +147 -0
  67. wexample_filestate_python/utils/python_type_checking_utils.py +113 -0
  68. wexample_filestate_python/utils/relocate_imports/__init__.py +7 -0
  69. wexample_filestate_python/utils/relocate_imports/__pycache__/__init__.py +0 -0
  70. wexample_filestate_python/utils/relocate_imports/python_import_rewriter.py +413 -0
  71. wexample_filestate_python/utils/relocate_imports/python_localize_runtime_imports.py +324 -0
  72. wexample_filestate_python/utils/relocate_imports/python_parser_import_index.py +80 -0
  73. wexample_filestate_python/utils/relocate_imports/python_runtime_symbol_collector.py +33 -0
  74. wexample_filestate_python/utils/relocate_imports/python_usage_collector.py +410 -0
  75. wexample_filestate_python/workdir/__init__.py +0 -0
  76. wexample_filestate_python/workdir/__pycache__/__init__.py +0 -0
  77. wexample_filestate_python-0.0.48.dist-info/METADATA +191 -0
  78. wexample_filestate_python-0.0.48.dist-info/RECORD +80 -0
  79. wexample_filestate_python-0.0.48.dist-info/WHEEL +4 -0
  80. wexample_filestate_python-0.0.48.dist-info/entry_points.txt +4 -0
@@ -0,0 +1,131 @@
1
+ from __future__ import annotations
2
+
3
+ FLAG_NAME = "python-iterable-sort"
4
+
5
+
6
+ def reorder_flagged_iterables(src: str) -> str:
7
+ """Sort items of flagged iterable blocks (typically list literals) alphabetically.
8
+
9
+ - Looks for lines with '# filestate: python-iterable-sort'.
10
+ - Sorts the contiguous following element lines until a blank line or closing bracket.
11
+ - Preserves comments: a contiguous block of comment lines is attached to the
12
+ item immediately below it and moves with that item.
13
+ - Preserves indentation and commas; compares using a case-insensitive key on
14
+ the item line's stripped content.
15
+ - If already sorted, returns original src unchanged.
16
+ """
17
+ lines = src.splitlines()
18
+ if not lines:
19
+ return src
20
+
21
+ flag_lines = _find_flag_line_indices(src)
22
+ if not flag_lines:
23
+ return src
24
+
25
+ changed = False
26
+
27
+ def split_into_groups(block_lines: list[str]) -> list[list[str]]:
28
+ groups: list[list[str]] = []
29
+ pending_comments: list[str] = []
30
+ for ln in block_lines:
31
+ if ln.lstrip().startswith("#"):
32
+ pending_comments.append(ln)
33
+ continue
34
+ # item line
35
+ group = pending_comments + [ln]
36
+ groups.append(group)
37
+ pending_comments = []
38
+ # Any trailing comments without item are ignored for sorting and left in place
39
+ # (shouldn't occur in expected usage). If present, attach to last group to preserve.
40
+ if pending_comments:
41
+ if groups:
42
+ groups[-1].extend(pending_comments)
43
+ else:
44
+ groups.append(pending_comments)
45
+ return groups
46
+
47
+ def group_key(g: list[str]) -> str:
48
+ # Use the first non-comment line in group as key
49
+ for ln in g:
50
+ if not ln.lstrip().startswith("#"):
51
+ # Remove trailing comma for comparison but don't modify actual text
52
+ item = ln.strip()
53
+ if item.endswith(","):
54
+ item = item[:-1]
55
+ return item.lower()
56
+ return "" # fallback
57
+
58
+ for flag_idx in reversed(flag_lines):
59
+ start, end = _collect_iterable_block(lines, flag_idx)
60
+ if start >= end:
61
+ continue
62
+ block = lines[start:end]
63
+
64
+ groups = split_into_groups(block)
65
+ # Build current order keys for no-op detection
66
+ current_keys = [group_key(g) for g in groups]
67
+ sorted_groups = sorted(groups, key=group_key)
68
+ sorted_keys = [group_key(g) for g in sorted_groups]
69
+
70
+ if sorted_keys == current_keys:
71
+ continue
72
+
73
+ # Flatten groups back to lines
74
+ new_block: list[str] = []
75
+ for g in sorted_groups:
76
+ new_block.extend(g)
77
+
78
+ lines[start:end] = new_block
79
+ changed = True
80
+
81
+ return "\n".join(lines) if changed else src
82
+
83
+
84
+ def _collect_iterable_block(lines: list[str], flag_idx: int) -> tuple[int, int]:
85
+ """Given the index of the flag line, collect the contiguous item block range.
86
+
87
+ Returns (start_idx, end_idx_exclusive) of lines to sort. We start at the next
88
+ non-empty, non-comment line after the flag, and stop before the first blank
89
+ line or the closing bracket ']' at the same or lesser indentation level.
90
+ """
91
+ n = len(lines)
92
+ # Determine base indentation from the flag line
93
+ flag_line = lines[flag_idx]
94
+ base_indent = len(flag_line) - len(flag_line.lstrip(" \t"))
95
+
96
+ # Start scanning after the flag line
97
+ i = flag_idx + 1
98
+ # Skip immediate blank/comment lines (though the example shows none)
99
+ while i < n and (lines[i].strip() == "" or lines[i].lstrip().startswith("#")):
100
+ i += 1
101
+ start = i
102
+
103
+ # Scan until blank line or closing bracket ']' at indentation <= base
104
+ while i < n:
105
+ stripped = lines[i].strip()
106
+ # Stop at blank separator line
107
+ if stripped == "":
108
+ break
109
+ # Stop when list ends
110
+ curr_indent = len(lines[i]) - len(lines[i].lstrip(" \t"))
111
+ if stripped.startswith("]") and curr_indent <= base_indent:
112
+ break
113
+ # Stop if we encounter a trailing comment-only line
114
+ if lines[i].lstrip().startswith("#"):
115
+ break
116
+ i += 1
117
+
118
+ end = i
119
+ return start, end
120
+
121
+
122
+ def _find_flag_line_indices(src: str) -> list[int]:
123
+ """Return line indices where the iterable sort flag appears."""
124
+ from wexample_filestate.helpers.flag import flag_exists
125
+
126
+ lines = src.splitlines()
127
+ indices: list[int] = []
128
+ for i, line in enumerate(lines):
129
+ if flag_exists(FLAG_NAME, line):
130
+ indices.append(i)
131
+ return indices
@@ -0,0 +1,80 @@
1
+ from __future__ import annotations
2
+
3
+ import libcst as cst
4
+
5
+
6
+ def find_main_guard_blocks(module: cst.Module) -> list[tuple[int, cst.If]]:
7
+ """Return list of (index, IfNode) for all top-level __main__ guard blocks."""
8
+ res: list[tuple[int, cst.If]] = []
9
+ for i, stmt in enumerate(module.body):
10
+ if is_main_guard_if(stmt):
11
+ res.append((i, stmt))
12
+ return res
13
+
14
+
15
+ def is_main_guard_at_end(module: cst.Module) -> bool:
16
+ blocks = find_main_guard_blocks(module)
17
+ if not blocks:
18
+ return True
19
+ last_index = blocks[-1][0]
20
+ # Consider "at the end" if it's the last non-empty node (ignoring trailing blank lines)
21
+ # Find last non-empty node index
22
+ last_non_empty = -1
23
+ for i in range(len(module.body) - 1, -1, -1):
24
+ if not isinstance(module.body[i], cst.EmptyLine):
25
+ last_non_empty = i
26
+ break
27
+ return last_index == last_non_empty
28
+
29
+
30
+ def is_main_guard_if(node: cst.CSTNode) -> bool:
31
+ return isinstance(node, cst.If) and _is_name_eq_main(node.test)
32
+
33
+
34
+ def move_main_guard_to_end(module: cst.Module) -> cst.Module:
35
+ blocks = find_main_guard_blocks(module)
36
+ if not blocks:
37
+ return module
38
+
39
+ new_body = list(module.body)
40
+
41
+ # Remove all blocks first (from highest index to lowest)
42
+ removed: list[cst.If] = []
43
+ for idx, node in sorted(blocks, key=lambda t: t[0], reverse=True):
44
+ removed.append(new_body.pop(idx))
45
+ removed.reverse() # preserve original order
46
+
47
+ # Strip leading_lines of the first moved guard only if it would create extra blank lines at end
48
+ # In practice, we keep existing leading_lines to minimize diffs.
49
+ # Append guards at the end (before trailing EmptyLines, if any)
50
+ # Find insertion point: just before trailing EmptyLines
51
+ insert_at = len(new_body)
52
+ while insert_at > 0 and isinstance(new_body[insert_at - 1], cst.EmptyLine):
53
+ insert_at -= 1
54
+
55
+ for offset, node in enumerate(removed):
56
+ new_body.insert(insert_at + offset, node)
57
+
58
+ return module.with_changes(body=new_body)
59
+
60
+
61
+ def _is_name_eq_main(test: cst.BaseExpression) -> bool:
62
+ # Match patterns: __name__ == "__main__" or '__main__'
63
+ if not isinstance(test, cst.Comparison):
64
+ return False
65
+ # Expect a single comparator with Eq
66
+ if len(test.comparisons) != 1:
67
+ return False
68
+ comp = test.comparisons[0]
69
+ if not isinstance(comp.operator, cst.Equal):
70
+ return False
71
+ # Left should be Name("__name__") (optionally with parentheses tolerated by CST?)
72
+ left_ok = isinstance(test.left, cst.Name) and test.left.value == "__name__"
73
+ if not left_ok:
74
+ return False
75
+ # Right should be SimpleString of __main__
76
+ right = comp.comparator
77
+ if isinstance(right, cst.SimpleString):
78
+ s = right.evaluated_value # libcst provides unescaped python value
79
+ return s == "__main__"
80
+ return False
@@ -0,0 +1,147 @@
1
+ from __future__ import annotations
2
+
3
+ import libcst as cst
4
+
5
+ # Common, recognized module metadata names
6
+ METADATA_NAMES: tuple[str, ...] = (
7
+ "__all__",
8
+ "__version__",
9
+ "__author__",
10
+ "__email__",
11
+ "__license__",
12
+ "__copyright__",
13
+ "__title__",
14
+ "__description__",
15
+ )
16
+
17
+
18
+ def find_module_metadata_statements(
19
+ module: cst.Module,
20
+ ) -> list[tuple[int, cst.SimpleStatementLine, str]]:
21
+ """Find all module-level metadata assignments.
22
+
23
+ Returns list of tuples: (index_in_body, node, metadata_name)
24
+ """
25
+ results: list[tuple[int, cst.SimpleStatementLine, str]] = []
26
+ for i, stmt in enumerate(module.body):
27
+ name = _get_assignment_target_name(stmt)
28
+ if name is not None:
29
+ assert isinstance(stmt, cst.SimpleStatementLine)
30
+ results.append((i, stmt, name))
31
+ return results
32
+
33
+
34
+ def group_and_sort_module_metadata(module: cst.Module) -> cst.Module:
35
+ """Group and sort module metadata assignments by variable name (A–Z).
36
+
37
+ - Collect all recognized metadata assignments at module level
38
+ - Remove them from their current positions
39
+ - Sort them case-insensitively by the metadata name
40
+ - Insert them as a contiguous block at the target index
41
+ - Avoid introducing extra blank lines by clearing leading_lines
42
+ """
43
+ found = find_module_metadata_statements(module)
44
+ if not found:
45
+ return module
46
+
47
+ # Determine insertion index before we mutate the body
48
+ insert_at = target_index_for_module_metadata(module)
49
+
50
+ # Remove from body (reverse order) and collect nodes with cleaned leading lines
51
+ to_remove_indices = sorted([i for i, _, _ in found], reverse=True)
52
+ new_body: list[cst.CSTNode] = list(module.body)
53
+ moved: list[tuple[str, cst.SimpleStatementLine]] = []
54
+
55
+ for idx in to_remove_indices:
56
+ node = new_body.pop(idx)
57
+ assert isinstance(node, cst.SimpleStatementLine)
58
+ name = _get_assignment_target_name(node)
59
+ if name is None:
60
+ # Should not happen; skip
61
+ continue
62
+ moved.append((name, node.with_changes(leading_lines=[])))
63
+
64
+ # Sort moved by name case-insensitively, '_' after letters rule not needed here
65
+ moved.sort(key=lambda t: t[0].lower())
66
+
67
+ # Adjust insertion index after removals that occurred before it
68
+ num_removed_before = sum(1 for idx in to_remove_indices if idx < insert_at)
69
+ adjusted_insert = insert_at - num_removed_before
70
+
71
+ # Insert in order
72
+ for offset, (_, node) in enumerate(moved):
73
+ new_body.insert(adjusted_insert + offset, node)
74
+
75
+ return module.with_changes(body=new_body)
76
+
77
+
78
+ def target_index_for_module_metadata(module: cst.Module) -> int:
79
+ """Compute target index to insert grouped module metadata.
80
+
81
+ According to file-level ordering:
82
+ - after imports
83
+ - after TYPE_CHECKING block
84
+ - then module metadata
85
+
86
+ So we insert after the last TYPE_CHECKING block if present, else after last regular
87
+ import if present, else after last __future__ import, else after docstring, else 0.
88
+ """
89
+ from wexample_filestate_python.utils.python_type_checking_utils import (
90
+ _is_future_import,
91
+ _is_regular_import,
92
+ find_type_checking_blocks,
93
+ )
94
+
95
+ last_type_checking = -1
96
+ for idx, _if in find_type_checking_blocks(module):
97
+ last_type_checking = max(last_type_checking, idx)
98
+
99
+ last_regular_import = -1
100
+ last_future_import = -1
101
+ for i, stmt in enumerate(module.body):
102
+ if _is_regular_import(stmt):
103
+ last_regular_import = i
104
+ elif _is_future_import(stmt):
105
+ last_future_import = i
106
+
107
+ if last_type_checking != -1:
108
+ return last_type_checking + 1
109
+ if last_regular_import != -1:
110
+ return last_regular_import + 1
111
+ if last_future_import != -1:
112
+ return last_future_import + 1
113
+ if module.has_docstring:
114
+ return 1
115
+ return 0
116
+
117
+
118
+ def _get_assignment_target_name(stmt: cst.CSTNode) -> str | None:
119
+ """Return the variable name if this statement is an assignment to a metadata name.
120
+
121
+ Only supports simple module-level assignments like `__version__ = ...` or
122
+ annotated assignment `__version__: str = ...`. Ignores destructuring or multiple targets.
123
+ """
124
+ if not isinstance(stmt, cst.SimpleStatementLine):
125
+ return None
126
+
127
+ if len(stmt.body) != 1:
128
+ return None
129
+
130
+ small = stmt.body[0]
131
+
132
+ if isinstance(small, cst.Assign):
133
+ # Only allow single target
134
+ if len(small.targets) != 1:
135
+ return None
136
+ target = small.targets[0].target
137
+ if isinstance(target, cst.Name) and target.value in METADATA_NAMES:
138
+ return target.value
139
+ return None
140
+
141
+ if isinstance(small, cst.AnnAssign):
142
+ target = small.target
143
+ if isinstance(target, cst.Name) and target.value in METADATA_NAMES:
144
+ return target.value
145
+ return None
146
+
147
+ return None
@@ -0,0 +1,113 @@
1
+ from __future__ import annotations
2
+
3
+ import libcst as cst
4
+
5
+
6
+ def find_type_checking_blocks(module: cst.Module) -> list[tuple[int, cst.If]]:
7
+ """Return list of (index, IfNode) for all top-level `if TYPE_CHECKING:` blocks."""
8
+ results: list[tuple[int, cst.If]] = []
9
+ for i, stmt in enumerate(module.body):
10
+ if isinstance(stmt, cst.If) and _is_type_checking_test(stmt.test):
11
+ results.append((i, stmt))
12
+ return results
13
+
14
+
15
+ def move_type_checking_blocks_after_imports(module: cst.Module) -> cst.Module:
16
+ """Move all `if TYPE_CHECKING:` blocks to just after regular imports.
17
+ Preserves the order of the blocks and removes extra leading blank lines.
18
+ """
19
+ blocks = find_type_checking_blocks(module)
20
+ if not blocks:
21
+ return module
22
+
23
+ # Determine target position before removal to avoid index shifts
24
+ insert_at = target_index_for_type_checking(module)
25
+
26
+ # Remove blocks from body (from highest index to lowest to keep indices valid)
27
+ remove_indices = sorted((i for i, _ in blocks), reverse=True)
28
+ new_body = list(module.body)
29
+ moved_blocks: list[cst.If] = []
30
+ for idx in remove_indices:
31
+ node = new_body.pop(idx)
32
+ assert isinstance(node, cst.If)
33
+ # Strip leading lines to avoid introducing blank lines
34
+ moved_blocks.append(node.with_changes(leading_lines=[]))
35
+
36
+ # We removed in reverse; preserve original relative order by reversing back
37
+ moved_blocks.reverse()
38
+
39
+ # Adjust insert_at for prior removals that occurred before it
40
+ num_removed_before = sum(1 for idx in remove_indices if idx < insert_at)
41
+ adjusted_insert = insert_at - num_removed_before
42
+
43
+ for offset, block in enumerate(moved_blocks):
44
+ new_body.insert(adjusted_insert + offset, block)
45
+
46
+ return module.with_changes(body=new_body)
47
+
48
+
49
+ def target_index_for_type_checking(module: cst.Module) -> int:
50
+ """Compute target insertion index for TYPE_CHECKING blocks.
51
+ After last regular import if any; otherwise after last __future__ import;
52
+ otherwise after module docstring if present; else position 0.
53
+ """
54
+ last_regular_import = -1
55
+ last_future_import = -1
56
+
57
+ for i, stmt in enumerate(module.body):
58
+ if _is_regular_import(stmt):
59
+ last_regular_import = i
60
+ elif _is_future_import(stmt):
61
+ last_future_import = i
62
+ else:
63
+ # stop scanning when hitting first non-import after having passed imports?
64
+ # We still record all to get the last occurrence.
65
+ pass
66
+
67
+ if last_regular_import != -1:
68
+ return last_regular_import + 1
69
+ if last_future_import != -1:
70
+ return last_future_import + 1
71
+
72
+ # Module docstring at index 0?
73
+ if module.has_docstring:
74
+ return 1
75
+
76
+ return 0
77
+
78
+
79
+ def _is_future_import(stmt: cst.CSTNode) -> bool:
80
+ if isinstance(stmt, cst.SimpleStatementLine):
81
+ for small in stmt.body:
82
+ if isinstance(small, cst.ImportFrom):
83
+ # from __future__ import ...
84
+ mod = small.module
85
+ if isinstance(mod, cst.Name) and mod.value == "__future__":
86
+ return True
87
+ return False
88
+
89
+
90
+ def _is_regular_import(stmt: cst.CSTNode) -> bool:
91
+ if isinstance(stmt, cst.SimpleStatementLine):
92
+ for small in stmt.body:
93
+ if isinstance(small, (cst.Import, cst.ImportFrom)):
94
+ # Exclude __future__
95
+ if isinstance(small, cst.ImportFrom):
96
+ mod = small.module
97
+ if isinstance(mod, cst.Name) and mod.value == "__future__":
98
+ return False
99
+ return True
100
+ return False
101
+
102
+
103
+ def _is_type_checking_test(test: cst.BaseExpression) -> bool:
104
+ # TYPE_CHECKING
105
+ if isinstance(test, cst.Name) and test.value == "TYPE_CHECKING":
106
+ return True
107
+ # typing.TYPE_CHECKING
108
+ if isinstance(test, cst.Attribute):
109
+ if isinstance(test.attr, cst.Name) and test.attr.value == "TYPE_CHECKING":
110
+ # Optional: enforce base name 'typing'
111
+ if isinstance(test.value, cst.Name) and test.value.value == "typing":
112
+ return True
113
+ return False
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from .python_parser_import_index import PythonParserImportIndex
4
+
5
+ __all__ = [
6
+ "PythonParserImportIndex",
7
+ ]