Mercurial > public > mercurial-scm > hg
comparison mercurial/cmdutil.py @ 52604:89215c5b714c
cmdutil: switch the `mode` on `cmdutil.makefileobj()` to str
I think the typing around whether `open()` returns `IO[bytes]` or `IO[str]`
hinges on the content of the mode string. Converting from bytes instead of
using a literal can suppress that (though PyCharm currently complains about
this). Instead, we can mandate the use of a (vastly reduced) set of mode
options. For now, none of the 3 callers provide this argument, so it's not a
big deal. Ideally, this would always enforce binary mode.
There's a little extra typing required to pull this off. The `_unclosablefile`
class can't subclass `typing.BinaryIO`, because there were a bunch of test
failures around writing to stdout. Strangely, pytype didn't complain that the
abstract methods on `typing.BinaryIO` weren't overridden in this case. Whatever
was going on, it's a simple proxy class, so we can just cast to the expected
type in the one place it is used.
author | Matt Harbison <matt_harbison@yahoo.com> |
---|---|
date | Mon, 16 Dec 2024 21:50:24 -0500 |
parents | 9d79ffeed7c0 |
children | 24ee91ba9aa8 |
comparison
equal
deleted
inserted
replaced
52603:a19f102dd377 | 52604:89215c5b714c |
---|---|
15 import typing | 15 import typing |
16 | 16 |
17 from typing import ( | 17 from typing import ( |
18 Any, | 18 Any, |
19 AnyStr, | 19 AnyStr, |
20 BinaryIO, | |
20 Dict, | 21 Dict, |
21 Iterable, | 22 Iterable, |
23 Literal, | |
22 Optional, | 24 Optional, |
23 TYPE_CHECKING, | 25 TYPE_CHECKING, |
24 cast, | 26 cast, |
25 ) | 27 ) |
26 | 28 |
1358 _(b"invalid format spec '%%%s' in output filename") % c | 1360 _(b"invalid format spec '%%%s' in output filename") % c |
1359 ) | 1361 ) |
1360 return b''.join(newname) | 1362 return b''.join(newname) |
1361 | 1363 |
1362 | 1364 |
1363 def makefilename(ctx, pat, **props): | 1365 def makefilename(ctx, pat: bytes, **props): |
1364 if not pat: | 1366 if not pat: |
1365 return pat | 1367 return pat |
1366 tmpl = _buildfntemplate(pat, **props) | 1368 tmpl = _buildfntemplate(pat, **props) |
1367 # BUG: alias expansion shouldn't be made against template fragments | 1369 # BUG: alias expansion shouldn't be made against template fragments |
1368 # rewritten from %-format strings, but we have no easy way to partially | 1370 # rewritten from %-format strings, but we have no easy way to partially |
1374 """True if the given pat looks like a filename denoting stdin/stdout""" | 1376 """True if the given pat looks like a filename denoting stdin/stdout""" |
1375 return not pat or pat == b'-' | 1377 return not pat or pat == b'-' |
1376 | 1378 |
1377 | 1379 |
1378 class _unclosablefile: | 1380 class _unclosablefile: |
1379 def __init__(self, fp): | 1381 def __init__(self, fp: BinaryIO) -> None: |
1380 self._fp = fp | 1382 self._fp = fp |
1381 | 1383 |
1382 def close(self): | 1384 def close(self): |
1383 pass | 1385 pass |
1384 | 1386 |
1393 | 1395 |
1394 def __exit__(self, exc_type, exc_value, exc_tb): | 1396 def __exit__(self, exc_type, exc_value, exc_tb): |
1395 pass | 1397 pass |
1396 | 1398 |
1397 | 1399 |
1398 def makefileobj(ctx, pat, mode=b'wb', **props): | 1400 def makefileobj( |
1399 writable = mode not in (b'r', b'rb') | 1401 ctx, pat: bytes, mode: Literal['rb', 'wb'] = 'wb', **props |
1402 ) -> BinaryIO: | |
1403 writable = mode not in ('r', 'rb') | |
1400 | 1404 |
1401 if isstdiofilename(pat): | 1405 if isstdiofilename(pat): |
1402 repo = ctx.repo() | 1406 repo = ctx.repo() |
1403 if writable: | 1407 if writable: |
1404 fp = repo.ui.fout | 1408 fp = repo.ui.fout |
1405 else: | 1409 else: |
1406 fp = repo.ui.fin | 1410 fp = repo.ui.fin |
1407 return _unclosablefile(fp) | 1411 return typing.cast(BinaryIO, _unclosablefile(fp)) |
1408 fn = makefilename(ctx, pat, **props) | 1412 fn = makefilename(ctx, pat, **props) |
1409 return open(fn, pycompat.sysstr(mode)) | 1413 return open(fn, mode) |
1410 | 1414 |
1411 | 1415 |
1412 def openstorage(repo, cmd, file_, opts, returnrevlog=False): | 1416 def openstorage(repo, cmd, file_, opts, returnrevlog=False): |
1413 """opens the changelog, manifest, a filelog or a given revlog""" | 1417 """opens the changelog, manifest, a filelog or a given revlog""" |
1414 cl = opts[b'changelog'] | 1418 cl = opts[b'changelog'] |