|
3 | 3 | For the license, see /licenses/black |
4 | 4 | """ |
5 | 5 |
|
| 6 | +import io |
6 | 7 | import os |
7 | 8 | import sys |
8 | 9 | from functools import lru_cache |
9 | 10 | from pathlib import Path |
10 | 11 |
|
11 | 12 | import tomli |
| 13 | +from black import Encoding, FileContent, Mode, NewLine, Preview, tokenize |
12 | 14 | from pathspec import PathSpec |
13 | 15 |
|
14 | 16 |
|
@@ -258,3 +260,38 @@ def gen_python_files( |
258 | 260 | include_match = include.search(normalized_path) if include else True |
259 | 261 | if include_match: |
260 | 262 | yield child |
| 263 | + |
| 264 | + |
| 265 | +def decode_bytes(src: bytes, mode: Mode) -> tuple[FileContent, Encoding, NewLine]: |
| 266 | + """Return a tuple of (decoded_contents, encoding, newline). |
| 267 | +
|
| 268 | + `newline` is either CRLF or LF but `decoded_contents` is decoded with |
| 269 | + universal newlines (i.e. only contains LF). |
| 270 | + """ |
| 271 | + srcbuf = io.BytesIO(src) |
| 272 | + encoding, lines = tokenize.detect_encoding(srcbuf.readline) |
| 273 | + if not lines: |
| 274 | + return "", encoding, "\n" |
| 275 | + |
| 276 | + if Preview.normalize_cr_newlines in mode: |
| 277 | + if lines[0][-2:] == b"\r\n": |
| 278 | + if b"\r" in lines[0][:-2]: |
| 279 | + newline = "\r" |
| 280 | + else: |
| 281 | + newline = "\r\n" |
| 282 | + elif lines[0][-1:] == b"\n": |
| 283 | + if b"\r" in lines[0][:-1]: |
| 284 | + newline = "\r" |
| 285 | + else: |
| 286 | + newline = "\n" |
| 287 | + else: |
| 288 | + if b"\r" in lines[0]: |
| 289 | + newline = "\r" |
| 290 | + else: |
| 291 | + newline = "\n" |
| 292 | + else: |
| 293 | + newline = "\r\n" if lines[0][-2:] == b"\r\n" else "\n" |
| 294 | + |
| 295 | + srcbuf.seek(0) |
| 296 | + with io.TextIOWrapper(srcbuf, encoding) as tiow: |
| 297 | + return tiow.read(), encoding, newline |
0 commit comments