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
pyflyby/_importstmt.py ADDED
@@ -0,0 +1,662 @@
1
+ # pyflyby/_importstmt.py.
2
+ # Copyright (C) 2011, 2012, 2013, 2014 Karl Chen.
3
+ # License: MIT http://opensource.org/licenses/MIT
4
+
5
+
6
+
7
+ import ast
8
+ from collections import namedtuple
9
+ from functools import total_ordering
10
+
11
+ from pyflyby._flags import CompilerFlags
12
+ from pyflyby._format import FormatParams, pyfill
13
+ from pyflyby._idents import is_identifier
14
+ from pyflyby._parse import PythonStatement
15
+ from pyflyby._util import (Inf, cached_attribute, cmp,
16
+ longest_common_prefix)
17
+
18
+
19
+ from typing import Dict, Tuple, Optional, Union
20
+
21
+
22
+
23
+ def read_black_config() -> Dict:
24
+ """Read the black configuration from ``pyproject.toml``"""
25
+ from black.files import find_pyproject_toml, parse_pyproject_toml
26
+
27
+ pyproject_path = find_pyproject_toml((".",))
28
+
29
+ raw_config = parse_pyproject_toml(pyproject_path) if pyproject_path else {}
30
+
31
+ config = {}
32
+ for key in [
33
+ "line_length",
34
+ "skip_magic_trailing_comma",
35
+ "skip_string_normalization",
36
+ ]:
37
+ if key in raw_config:
38
+ config[key] = raw_config[key]
39
+ if "target_version" in raw_config:
40
+ target_version = raw_config["target_version"]
41
+ if isinstance(target_version, str):
42
+ config["target_version"] = target_version
43
+ elif isinstance(target_version, list):
44
+ # Convert TOML list to a Python set
45
+ config["target_version"] = set(target_version)
46
+ else:
47
+ raise ValueError(
48
+ f"Invalid config for black = {target_version!r} in {pyproject_path}"
49
+ )
50
+ return config
51
+
52
+
53
+ class ImportFormatParams(FormatParams):
54
+ align_imports:Union[bool, set, list, tuple, str] = True
55
+ """
56
+ Whether and how to align 'from modulename import aliases...'. If ``True``,
57
+ then the 'import' keywords will be aligned within a block. If an integer,
58
+ then the 'import' keyword will always be at that column. They will be
59
+ wrapped if necessary.
60
+ """
61
+
62
+ from_spaces:int = 1
63
+ """
64
+ The number of spaces after the 'from' keyword. (Must be at least 1.)
65
+ """
66
+
67
+ separate_from_imports:bool = True
68
+ """
69
+ Whether all 'from ... import ...' in an import block should come after
70
+ 'import ...' statements. ``separate_from_imports = False`` works well with
71
+ ``from_spaces = 3``. ('from __future__ import ...' always comes first.)
72
+ """
73
+
74
+ align_future:bool = False
75
+ """
76
+ Whether 'from __future__ import ...' statements should be aligned with
77
+ others. If False, uses a single space after the 'from' and 'import'
78
+ keywords.
79
+ """
80
+
81
+
82
+ class NonImportStatementError(TypeError):
83
+ """
84
+ Unexpectedly got a statement that wasn't an import.
85
+ """
86
+
87
+ ImportSplit = namedtuple("ImportSplit",
88
+ "module_name member_name import_as")
89
+ """
90
+ Representation of a single import at the token level::
91
+
92
+ from [...]<module_name> import <member_name> as <import_as>
93
+
94
+ If <module_name> is ``None``, then there is no "from" clause; instead just::
95
+ import <member_name> as <import_as>
96
+ """
97
+
98
+ @total_ordering
99
+ class Import(object):
100
+ """
101
+ Representation of the desire to import a single name into the current
102
+ namespace.
103
+
104
+ >>> Import.from_parts(".foo.bar", "bar")
105
+ Import('from .foo import bar')
106
+
107
+ >>> Import("from . import foo")
108
+ Import('from . import foo')
109
+
110
+ >>> Import("from . import foo").fullname
111
+ '.foo'
112
+
113
+ >>> Import("import foo . bar")
114
+ Import('import foo.bar')
115
+
116
+ >>> Import("import foo . bar as baz")
117
+ Import('from foo import bar as baz')
118
+
119
+ >>> Import("import foo . bar as bar")
120
+ Import('from foo import bar')
121
+
122
+ >>> Import("foo.bar")
123
+ Import('from foo import bar')
124
+
125
+ """
126
+
127
+ fullname:str
128
+ import_as:str
129
+
130
+ def __new__(cls, arg):
131
+ if isinstance(arg, cls):
132
+ return arg
133
+ if isinstance(arg, ImportSplit):
134
+ return cls.from_split(arg)
135
+ if isinstance(arg, (ImportStatement, PythonStatement)):
136
+ return cls._from_statement(arg)
137
+ if isinstance(arg, str):
138
+ return cls._from_identifier_or_statement(arg)
139
+ raise TypeError
140
+
141
+ @classmethod
142
+ def from_parts(cls, fullname, import_as):
143
+ assert isinstance(fullname, str)
144
+ assert isinstance(import_as, str)
145
+ self = object.__new__(cls)
146
+ self.fullname = fullname
147
+ self.import_as = import_as
148
+ return self
149
+
150
+ @classmethod
151
+ def _from_statement(cls, statement):
152
+ """
153
+ :type statement:
154
+ `ImportStatement` or convertible (`PythonStatement`, ``str``)
155
+ :rtype:
156
+ `Import`
157
+ """
158
+ statement = ImportStatement(statement)
159
+ imports = statement.imports
160
+ if len(imports) != 1:
161
+ raise ValueError(
162
+ "Got %d imports instead of 1 in %r" % (len(imports), statement))
163
+ return imports[0]
164
+
165
+ @classmethod
166
+ def _from_identifier_or_statement(cls, arg):
167
+ """
168
+ Parse either a raw identifier or a statement.
169
+
170
+ >>> Import._from_identifier_or_statement('foo.bar.baz')
171
+ Import('from foo.bar import baz')
172
+
173
+ >>> Import._from_identifier_or_statement('import foo.bar.baz')
174
+ Import('import foo.bar.baz')
175
+
176
+ :rtype:
177
+ `Import`
178
+ """
179
+ if is_identifier(arg, dotted=True):
180
+ return cls.from_parts(arg, arg.split('.')[-1])
181
+ else:
182
+ return cls._from_statement(arg)
183
+
184
+ @cached_attribute
185
+ def split(self):
186
+ """
187
+ Split this `Import` into a ``ImportSplit`` which represents the
188
+ token-level ``module_name``, ``member_name``, ``import_as``.
189
+
190
+ Note that at the token level, ``import_as`` can be ``None`` to represent
191
+ that the import statement doesn't have an "as ..." clause, whereas the
192
+ ``import_as`` attribute on an ``Import`` object is never ``None``.
193
+
194
+ >>> Import.from_parts(".foo.bar", "bar").split
195
+ ImportSplit(module_name='.foo', member_name='bar', import_as=None)
196
+
197
+ >>> Import("from . import foo").split
198
+ ImportSplit(module_name='.', member_name='foo', import_as=None)
199
+
200
+ >>> Import.from_parts(".foo", "foo").split
201
+ ImportSplit(module_name='.', member_name='foo', import_as=None)
202
+
203
+ >>> Import.from_parts("foo.bar", "foo.bar").split
204
+ ImportSplit(module_name=None, member_name='foo.bar', import_as=None)
205
+
206
+ :rtype:
207
+ `ImportSplit`
208
+ """
209
+ if self.import_as == self.fullname:
210
+ return ImportSplit(None, self.fullname, None)
211
+ level = 0
212
+ qname = self.fullname
213
+ for level, char in enumerate(qname):
214
+ if char != '.':
215
+ break
216
+ prefix = qname[:level]
217
+ qname = qname[level:]
218
+ if '.' in qname:
219
+ module_name, member_name = qname.rsplit(".", 1)
220
+ else:
221
+ module_name = ''
222
+ member_name = qname
223
+ module_name = prefix + module_name
224
+ import_as = self.import_as
225
+ if import_as == member_name:
226
+ import_as = None
227
+ return ImportSplit(module_name or None, member_name, import_as)
228
+
229
+ @classmethod
230
+ def from_split(cls, impsplit):
231
+ """
232
+ Construct an `Import` instance from ``module_name``, ``member_name``,
233
+ ``import_as``.
234
+
235
+ :rtype:
236
+ `Import`
237
+ """
238
+ impsplit = ImportSplit(*impsplit)
239
+ module_name, member_name, import_as = impsplit
240
+ if import_as is None:
241
+ import_as = member_name
242
+ if module_name is None:
243
+ result = cls.from_parts(member_name, import_as)
244
+ else:
245
+ fullname = "%s%s%s" % (
246
+ module_name,
247
+ "" if module_name.endswith(".") else ".",
248
+ member_name)
249
+ result = cls.from_parts(fullname, import_as)
250
+ # result.split will usually be the same as impsplit, but could be
251
+ # different if the input was 'import foo.bar as baz', which we
252
+ # canonicalize to 'from foo import bar as baz'.
253
+ return result
254
+
255
+ def prefix_match(self, imp):
256
+ """
257
+ Return the longest common prefix between ``self`` and ``imp``.
258
+
259
+ >>> Import("import ab.cd.ef").prefix_match(Import("import ab.cd.xy"))
260
+ ('ab', 'cd')
261
+
262
+ :type imp:
263
+ `Import`
264
+ :rtype:
265
+ ``tuple`` of ``str``
266
+ """
267
+ imp = Import(imp)
268
+ n1 = self.fullname.split('.')
269
+ n2 = imp.fullname.split('.')
270
+ return tuple(longest_common_prefix(n1, n2))
271
+
272
+ def replace(self, prefix, replacement):
273
+ """
274
+ Return a new ``Import`` that replaces ``prefix`` with ``replacement``.
275
+
276
+ >>> Import("from aa.bb import cc").replace("aa.bb", "xx.yy")
277
+ Import('from xx.yy import cc')
278
+
279
+ >>> Import("from aa import bb").replace("aa.bb", "xx.yy")
280
+ Import('from xx import yy as bb')
281
+
282
+ :rtype:
283
+ ``Import``
284
+ """
285
+ prefix_parts = prefix.split('.')
286
+ replacement_parts = replacement.split('.')
287
+ fullname_parts = self.fullname.split('.')
288
+ if fullname_parts[:len(prefix_parts)] != prefix_parts:
289
+ # No prefix match.
290
+ return self
291
+ fullname_parts[:len(prefix_parts)] = replacement_parts
292
+ import_as_parts = self.import_as.split('.')
293
+ if import_as_parts[:len(prefix_parts)] == prefix_parts:
294
+ import_as_parts[:len(prefix_parts)] = replacement_parts
295
+ return self.from_parts('.'.join(fullname_parts),
296
+ '.'.join(import_as_parts))
297
+
298
+ @cached_attribute
299
+ def flags(self):
300
+ """
301
+ If this is a __future__ import, then the compiler_flag associated with
302
+ it. Otherwise, 0.
303
+ """
304
+ if self.split.module_name == "__future__":
305
+ return CompilerFlags(self.split.member_name)
306
+ else:
307
+ return CompilerFlags.from_int(0)
308
+
309
+ @property
310
+ def _data(self):
311
+ return (self.fullname, self.import_as)
312
+
313
+ def pretty_print(self, params=FormatParams()):
314
+ return ImportStatement([self]).pretty_print(params)
315
+
316
+ def __str__(self):
317
+ return self.pretty_print(FormatParams(max_line_length=Inf)).rstrip()
318
+
319
+ def __repr__(self):
320
+ return "%s(%r)" % (type(self).__name__, str(self))
321
+
322
+ def __hash__(self):
323
+ return hash(self._data)
324
+
325
+ def __cmp__(self, other):
326
+ if self is other:
327
+ return 0
328
+ if not isinstance(other, Import):
329
+ return NotImplemented
330
+ return cmp(self._data, other._data)
331
+
332
+ def __eq__(self, other):
333
+ if self is other:
334
+ return True
335
+ if not isinstance(other, Import):
336
+ return NotImplemented
337
+ return self._data == other._data
338
+
339
+ def __ne__(self, other):
340
+ return not (self == other)
341
+
342
+ # The rest are defined by total_ordering
343
+ def __lt__(self, other):
344
+ if self is other:
345
+ return False
346
+ if not isinstance(other, Import):
347
+ return NotImplemented
348
+ return self._data < other._data
349
+
350
+ def _validate_alias(arg) -> Tuple[str, Optional[str]]:
351
+ """
352
+ Ensure each alias is a tuple (str, None|str), and return it.
353
+
354
+ """
355
+ assert isinstance(arg, tuple)
356
+ # Pyright does not seem to be able to infer the length from a
357
+ # the unpacking.
358
+ assert len(arg) == 2
359
+ a0, a1 = arg
360
+ assert isinstance(a0, str)
361
+ assert isinstance(a1, (str, type(None)))
362
+ return arg
363
+
364
+ @total_ordering
365
+ class ImportStatement:
366
+ """
367
+ Token-level representation of an import statement containing multiple
368
+ imports from a single module. Corresponds to an ``ast.ImportFrom`` or
369
+ ``ast.Import``.
370
+ """
371
+
372
+ aliases : Tuple[Tuple[str, Optional[str]],...]
373
+ fromname : Optional[str]
374
+
375
+ def __new__(cls, arg):
376
+ if isinstance(arg, cls):
377
+ return arg
378
+ if isinstance(arg, str):
379
+ return cls._from_str(arg)
380
+ if isinstance(arg, PythonStatement):
381
+ return cls._from_statement(arg)
382
+ if isinstance(arg, (ast.ImportFrom, ast.Import)):
383
+ return cls._from_ast_node(arg)
384
+ if isinstance(arg, Import):
385
+ return cls._from_imports([arg])
386
+ if isinstance(arg, (tuple, list)) and len(arg):
387
+ if isinstance(arg[0], Import):
388
+ return cls._from_imports(arg)
389
+ raise TypeError
390
+
391
+ @classmethod
392
+ def from_parts(cls, fromname:Optional[str], aliases:Tuple[Tuple[str, Optional[str]],...]):
393
+ assert isinstance(aliases, tuple)
394
+ assert len(aliases) > 0
395
+
396
+ self = object.__new__(cls)
397
+ self.fromname = fromname
398
+ self.aliases = tuple(_validate_alias(a) for a in aliases)
399
+ return self
400
+
401
+ @classmethod
402
+ def _from_str(cls, code:str, /):
403
+ """
404
+ >>> ImportStatement._from_str("from foo import bar, bar2, bar")
405
+ ImportStatement('from foo import bar, bar2, bar')
406
+
407
+ >>> ImportStatement._from_str("from foo import bar as bar")
408
+ ImportStatement('from foo import bar as bar')
409
+
410
+ >>> ImportStatement._from_str("from foo.bar import baz")
411
+ ImportStatement('from foo.bar import baz')
412
+
413
+ >>> ImportStatement._from_str("import foo.bar")
414
+ ImportStatement('import foo.bar')
415
+
416
+ >>> ImportStatement._from_str("from .foo import bar")
417
+ ImportStatement('from .foo import bar')
418
+
419
+ >>> ImportStatement._from_str("from . import bar, bar2")
420
+ ImportStatement('from . import bar, bar2')
421
+
422
+ :type statement:
423
+ `PythonStatement`
424
+ :rtype:
425
+ `ImportStatement`
426
+ """
427
+
428
+ statement = PythonStatement(code)
429
+ return cls._from_ast_node(statement.ast_node)
430
+
431
+ @classmethod
432
+ def _from_statement(cls, statement):
433
+ statement = PythonStatement.from_statement(statement)
434
+ return cls._from_ast_node(statement.ast_node)
435
+
436
+ @classmethod
437
+ def _from_ast_node(cls, node):
438
+ """
439
+ Construct an `ImportStatement` from an `ast` node.
440
+
441
+ :rtype:
442
+ `ImportStatement`
443
+ """
444
+ if isinstance(node, ast.ImportFrom):
445
+ if node.module is None:
446
+ module = ''
447
+ else:
448
+ assert isinstance(node.module, str)
449
+ module = node.module
450
+ fromname = '.' * node.level + module
451
+ elif isinstance(node, ast.Import):
452
+ fromname = None
453
+ else:
454
+ raise NonImportStatementError(
455
+ 'Expected ImportStatement, got {node}'.format(node=node)
456
+ )
457
+ aliases = tuple( (alias.name, alias.asname) for alias in node.names )
458
+ return cls.from_parts(fromname, aliases)
459
+
460
+ @classmethod
461
+ def _from_imports(cls, imports):
462
+ """
463
+ Construct an `ImportStatement` from a sequence of ``Import`` s. They
464
+ must all have the same ``fromname``.
465
+
466
+ :type imports:
467
+ Sequence of `Import` s
468
+ :rtype:
469
+ `ImportStatement`
470
+ """
471
+ if not all(isinstance(imp, Import) for imp in imports):
472
+ raise TypeError
473
+ if not len(imports) > 0:
474
+ raise ValueError
475
+ module_names = set(imp.split.module_name for imp in imports)
476
+ if len(module_names) > 1:
477
+ raise ValueError(
478
+ "Inconsistent module names %r" % (sorted(module_names),))
479
+ fromname = list(module_names)[0]
480
+ aliases = tuple(imp.split[1:] for imp in imports)
481
+ return cls.from_parts(fromname, aliases)
482
+
483
+ @cached_attribute
484
+ def imports(self):
485
+ """
486
+ Return a sequence of `Import` s.
487
+
488
+ :rtype:
489
+ ``tuple`` of `Import` s
490
+ """
491
+ return tuple(
492
+ Import.from_split((self.fromname, alias[0], alias[1]))
493
+ for alias in self.aliases)
494
+
495
+ @property
496
+ def module(self) -> Tuple[str, ...]:
497
+ """
498
+
499
+ return the import module as a list of string (which would be joined by
500
+ dot in the original import form.
501
+
502
+ This is useful for sorting purposes
503
+
504
+ Note that this may contain some empty string in particular with relative
505
+ imports
506
+ """
507
+ if self.fromname:
508
+ return tuple(self.fromname.split('.'))
509
+
510
+
511
+ assert len(self.aliases) == 1, self.aliases
512
+
513
+ return tuple(self.aliases[0][0].split('.'))
514
+
515
+
516
+ def _cmp(self):
517
+ """
518
+ Comparison function for sorting.
519
+
520
+ We want to sort:
521
+ - by the root module
522
+ - whether it is an "import ... as", or "from ... import as" import
523
+ - then lexicographically
524
+
525
+ """
526
+ return (self.module[0], 0 if self.fromname is not None else 1, self.fromname)
527
+
528
+ @cached_attribute
529
+ def flags(self):
530
+ """
531
+ If this is a __future__ import, then the bitwise-ORed of the
532
+ compiler_flag values associated with the features. Otherwise, 0.
533
+ """
534
+ return CompilerFlags(*[imp.flags for imp in self.imports])
535
+
536
+ def pretty_print(self, params=FormatParams(),
537
+ import_column=None, from_spaces=1):
538
+ """
539
+ Pretty-print into a single string.
540
+
541
+ :type params:
542
+ `FormatParams`
543
+ :param modulename_ljust:
544
+ Number of characters to left-justify the 'from' name.
545
+ :rtype:
546
+ ``str``
547
+ """
548
+ s0 = ''
549
+ s = ''
550
+ assert from_spaces >= 1
551
+ if self.fromname is not None:
552
+ s += "from%s%s " % (' ' * from_spaces, self.fromname)
553
+ if import_column is not None:
554
+ if len(s) > import_column:
555
+ # The caller wants the 'import' statement lined up left of
556
+ # where the current end of the line is. So wrap it
557
+ # specially like this::
558
+ # from foo import ...
559
+ # from foo.bar.baz \
560
+ # import ...
561
+ s0 = s + '\\\n'
562
+ s = ' ' * import_column
563
+ else:
564
+ s = s.ljust(import_column)
565
+ s += "import "
566
+ tokens = []
567
+ for importname, asname in self.aliases:
568
+ if asname is not None:
569
+ t = "%s as %s" % (importname, asname)
570
+ else:
571
+ t = "%s" % (importname,)
572
+ tokens.append(t)
573
+ res = s0 + pyfill(s, tokens, params=params)
574
+ if params.use_black:
575
+ return self.run_black(res, params)
576
+ return res
577
+
578
+ @staticmethod
579
+ def run_black(src_contents: str, params:FormatParams) -> str:
580
+ """Run the black formatter for the Python source code given as a string
581
+
582
+ This is adapted from https://github.com/akaihola/darker
583
+
584
+ """
585
+ from black import format_str, FileMode
586
+ from black.mode import TargetVersion
587
+
588
+ black_config = read_black_config()
589
+ mode = dict()
590
+ if "line_length" in black_config:
591
+ mode["line_length"] = (
592
+ params.max_line_length
593
+ if params.max_line_length
594
+ else black_config["line_length"]
595
+ )
596
+ if "target_version" in black_config:
597
+ if isinstance(black_config["target_version"], set):
598
+ target_versions_in = black_config["target_version"]
599
+ else:
600
+ target_versions_in = {black_config["target_version"]}
601
+ all_target_versions = {
602
+ tgt_v.name.lower(): tgt_v for tgt_v in TargetVersion
603
+ }
604
+ bad_target_versions = target_versions_in - set(all_target_versions)
605
+ if bad_target_versions:
606
+ raise ValueError(
607
+ f"Invalid target version(s) {bad_target_versions}"
608
+ )
609
+ mode["target_versions"] = {
610
+ all_target_versions[n] for n in target_versions_in
611
+ }
612
+ if "skip_magic_trailing_comma" in black_config:
613
+ mode["magic_trailing_comma"] = not black_config[
614
+ "skip_magic_trailing_comma"
615
+ ]
616
+ if "skip_string_normalization" in black_config:
617
+ # The ``black`` command line argument is
618
+ # ``--skip-string-normalization``, but the parameter for
619
+ # ``black.Mode`` needs to be the opposite boolean of
620
+ # ``skip-string-normalization``, hence the inverse boolean
621
+ mode["string_normalization"] = not black_config["skip_string_normalization"]
622
+
623
+ # The custom handling of empty and all-whitespace files below will be unnecessary if
624
+ # https://github.com/psf/black/pull/2484 lands in Black.
625
+ contents_for_black = src_contents
626
+ return format_str(contents_for_black, mode=FileMode(**mode))
627
+
628
+ @property
629
+ def _data(self):
630
+ return (self.fromname, self.aliases)
631
+
632
+ def __str__(self):
633
+ return self.pretty_print(FormatParams(max_line_length=Inf)).rstrip()
634
+
635
+ def __repr__(self):
636
+ return "%s(%r)" % (type(self).__name__, str(self))
637
+
638
+ def __cmp__(self, other):
639
+ if self is other:
640
+ return 0
641
+ if not isinstance(other, ImportStatement):
642
+ return NotImplemented
643
+ return cmp(self._data, other._data)
644
+
645
+ def __eq__(self, other):
646
+ if self is other:
647
+ return True
648
+ if not isinstance(other, ImportStatement):
649
+ return NotImplemented
650
+ return self._data == other._data
651
+
652
+ def __ne__(self, other):
653
+ return not (self == other)
654
+
655
+ # The rest are defined by total_ordering
656
+ def __lt__(self, other):
657
+ if not isinstance(other, ImportStatement):
658
+ return NotImplemented
659
+ return self._data < other._data
660
+
661
+ def __hash__(self):
662
+ return hash(self._data)