pyflyby 1.9.4__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.

Potentially problematic release.


This version of pyflyby might be problematic. Click here for more details.

Files changed (54) hide show
  1. pyflyby/__init__.py +56 -0
  2. pyflyby/__main__.py +9 -0
  3. pyflyby/_autoimp.py +2114 -0
  4. pyflyby/_cmdline.py +531 -0
  5. pyflyby/_comms.py +221 -0
  6. pyflyby/_dbg.py +1339 -0
  7. pyflyby/_docxref.py +379 -0
  8. pyflyby/_file.py +738 -0
  9. pyflyby/_flags.py +230 -0
  10. pyflyby/_format.py +182 -0
  11. pyflyby/_idents.py +233 -0
  12. pyflyby/_import_sorting.py +165 -0
  13. pyflyby/_importclns.py +642 -0
  14. pyflyby/_importdb.py +588 -0
  15. pyflyby/_imports2s.py +639 -0
  16. pyflyby/_importstmt.py +662 -0
  17. pyflyby/_interactive.py +2605 -0
  18. pyflyby/_livepatch.py +793 -0
  19. pyflyby/_log.py +199 -0
  20. pyflyby/_modules.py +515 -0
  21. pyflyby/_parse.py +1441 -0
  22. pyflyby/_py.py +2078 -0
  23. pyflyby/_util.py +459 -0
  24. pyflyby/_version.py +7 -0
  25. pyflyby/autoimport.py +20 -0
  26. pyflyby/importdb.py +19 -0
  27. pyflyby-1.9.4.data/data/etc/pyflyby/canonical.py +10 -0
  28. pyflyby-1.9.4.data/data/etc/pyflyby/common.py +27 -0
  29. pyflyby-1.9.4.data/data/etc/pyflyby/forget.py +10 -0
  30. pyflyby-1.9.4.data/data/etc/pyflyby/mandatory.py +10 -0
  31. pyflyby-1.9.4.data/data/etc/pyflyby/numpy.py +156 -0
  32. pyflyby-1.9.4.data/data/etc/pyflyby/std.py +335 -0
  33. pyflyby-1.9.4.data/data/libexec/pyflyby/colordiff +34 -0
  34. pyflyby-1.9.4.data/data/libexec/pyflyby/diff-colorize +148 -0
  35. pyflyby-1.9.4.data/data/share/doc/pyflyby/LICENSE.txt +23 -0
  36. pyflyby-1.9.4.data/data/share/doc/pyflyby/TODO.txt +115 -0
  37. pyflyby-1.9.4.data/data/share/doc/pyflyby/testing.txt +13 -0
  38. pyflyby-1.9.4.data/data/share/emacs/site-lisp/pyflyby.el +108 -0
  39. pyflyby-1.9.4.data/scripts/collect-exports +76 -0
  40. pyflyby-1.9.4.data/scripts/collect-imports +58 -0
  41. pyflyby-1.9.4.data/scripts/find-import +38 -0
  42. pyflyby-1.9.4.data/scripts/list-bad-xrefs +34 -0
  43. pyflyby-1.9.4.data/scripts/prune-broken-imports +34 -0
  44. pyflyby-1.9.4.data/scripts/pyflyby-diff +34 -0
  45. pyflyby-1.9.4.data/scripts/reformat-imports +27 -0
  46. pyflyby-1.9.4.data/scripts/replace-star-imports +37 -0
  47. pyflyby-1.9.4.data/scripts/tidy-imports +191 -0
  48. pyflyby-1.9.4.data/scripts/transform-imports +47 -0
  49. pyflyby-1.9.4.dist-info/LICENSE.txt +23 -0
  50. pyflyby-1.9.4.dist-info/METADATA +507 -0
  51. pyflyby-1.9.4.dist-info/RECORD +54 -0
  52. pyflyby-1.9.4.dist-info/WHEEL +5 -0
  53. pyflyby-1.9.4.dist-info/entry_points.txt +3 -0
  54. pyflyby-1.9.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,165 @@
1
+ """
2
+ This module contain utility functions to sort imports in a Python Block.
3
+ """
4
+ from __future__ import annotations
5
+
6
+ from collections import Counter
7
+ from dataclasses import dataclass
8
+ from itertools import groupby
9
+ from pyflyby._importstmt import ImportStatement, PythonStatement
10
+ from pyflyby._parse import PythonBlock
11
+ from typing import List, Tuple, Union
12
+
13
+
14
+ # @dataclass
15
+ # class ImportSection:
16
+ # """
17
+ # This represent an Import
18
+ # """
19
+ # sections: List[ImportGroup]
20
+
21
+
22
+ @dataclass
23
+ class ImportGroup:
24
+ """
25
+ Typically at the top of a file, the first import will be part
26
+ of an ImportGroup, and subsequent imports
27
+ will be other import groups which.
28
+
29
+ Import sorting will affect only the imports in the same group,
30
+ as we want import sorting to be indempotent. If import sorting
31
+ were migrating imports between groups, then imports could be
32
+ moved to sections were comments would nto be relevant.
33
+ """
34
+
35
+ imports: List[ImportStatement]
36
+
37
+ @classmethod
38
+ def from_statements(cls, statements: List[PythonStatement]) -> ImportGroup:
39
+ return ImportGroup([ImportStatement(s) for s in statements])
40
+
41
+ def sorted(self) -> ImportGroup:
42
+ """
43
+ return an ImportGroup with import sorted lexicographically.
44
+ """
45
+ return ImportGroup(sorted(self.imports, key=lambda x: x._cmp()))
46
+
47
+ def sorted_subgroups(self) -> List[Tuple[bool, List[ImportStatement]]]:
48
+ """
49
+ Return a list of subgroup keyed by module and whether they are the sole import
50
+ from this module.
51
+
52
+ From issue #13, we will want to sort import from the same package together when
53
+ there is more than one import and separat with a blank line
54
+
55
+ We also group all imports from a package that appear only once together.
56
+
57
+ For this we need both to know if the import is from a single import (the boolean).
58
+
59
+ Returns
60
+ -------
61
+ bool:
62
+ wether from a single import
63
+ List[ImportStatement]
64
+ The actual import.
65
+ """
66
+ c = Counter(imp.module[0] for imp in self.imports)
67
+ return [
68
+ (c[k] > 1, list(v)) for k, v in groupby(self.imports, lambda x: x.module[0])
69
+ ]
70
+
71
+
72
+ def split_import_groups(
73
+ statements: Tuple[PythonStatement],
74
+ ) -> List[Union[ImportGroup, PythonStatement]]:
75
+ """
76
+ Given a list of statements split into import groups.
77
+
78
+ One of the question is how to treat split with comments.
79
+
80
+ - Do blank lines after groups with comments start a new block
81
+ - Does the comment line create a complete new block.
82
+
83
+ In particular because we want this to be indempotent,
84
+ we can't move imports between groups.
85
+ """
86
+
87
+ # these are the import groups we'll
88
+ groups: List[PythonStatement | ImportGroup] = []
89
+
90
+ current_group: List[PythonStatement] = []
91
+ statemt_iterator = iter(statements)
92
+ for statement in statemt_iterator:
93
+ if statement.is_blank and current_group:
94
+ pass
95
+ # currently do nothing with whitespace while in import groups.
96
+ elif statement.is_import:
97
+ # push on top of current_comment.
98
+ current_group.append(statement)
99
+ else:
100
+ if current_group:
101
+ groups.append(ImportGroup.from_statements(current_group))
102
+ current_group = []
103
+ groups.append(statement)
104
+
105
+ # we should break of and populate rest
106
+ # We can't do anything if we encounter any non-import statement,
107
+ # as we do no know if it can be a conditional.
108
+ # technically I guess we coudl find another import block, and reorder these.
109
+ if current_group:
110
+ groups.append(ImportGroup.from_statements(current_group))
111
+
112
+ # this is an iterator, not an iterable, we exaust it to reify the rest.
113
+
114
+ # first group may be empty if the first line is a comment.
115
+ # We filter and sort relevant statements.
116
+ groups = [g for g in groups if groups]
117
+ sorted_groups: List[PythonStatement | ImportGroup] = [
118
+ g.sorted() if isinstance(g, ImportGroup) else g for g in groups
119
+ ]
120
+ return sorted_groups
121
+
122
+
123
+ def regroup(groups: List[ImportGroup | PythonStatement]) -> PythonBlock:
124
+ """
125
+ given import groups and list of statement, return an Python block with sorted import
126
+ """
127
+ res: str = ""
128
+ in_single = False
129
+ for group in groups:
130
+ if isinstance(group, ImportGroup):
131
+ # the subgroup here will be responsible for groups reordering.
132
+ for mult, subgroup in group.sorted_subgroups():
133
+ if mult:
134
+ if in_single:
135
+ res += "\n"
136
+ if not res.endswith("\n"):
137
+ res += "\n"
138
+ for x in subgroup:
139
+ res += x.pretty_print(import_column=30).rstrip() + "\n"
140
+ if not res.endswith("\n\n"):
141
+ res += "\n"
142
+ in_single = False
143
+ else:
144
+ assert len(subgroup) == 1
145
+ in_single = True
146
+ for sub in subgroup:
147
+ res += str(sub).rstrip() + "\n"
148
+
149
+ if not res.endswith("\n\n"):
150
+ res += "\n"
151
+ else:
152
+ if in_single and not res.endswith("\n\n"):
153
+ res += "\n"
154
+ in_single = False
155
+ res += str(PythonBlock(group))
156
+
157
+ return PythonBlock.concatenate([PythonBlock(res)])
158
+
159
+
160
+ def sort_imports(block: PythonBlock) -> PythonBlock:
161
+ assert isinstance(block, PythonBlock)
162
+ # we ignore below that block.statement can be a List[Unknown]
163
+ gs = split_import_groups(block.statements) # type: ignore
164
+ # TODO: math the other fileds like filename....
165
+ return regroup(gs)