##// END OF EJS Templates
interfaces: introduce and use a protocol class for the `bdiff` module...
interfaces: introduce and use a protocol class for the `bdiff` module This is allowed by PEP 544[1], and we basically follow the example there. The class here is copied from `mercurial.pure.bdiff`, and the implementation removed. There are several modules that have a few different implementations, and the implementation chosen is controlled by `HGMODULEPOLICY`. The module is loaded via `mercurial/policy.py`, and has been inferred by pytype as `Any` up to this point. Therefore it and PyCharm were blind to all functions on the module, and their signatures. Also, having multiple instances of the same module allows their signatures to get out of sync. Introducing a protocol class allows the loaded module that is stored in a variable to be given type info, which cascades through the various places it is used. This change alters 11 *.pyi files, for example. In theory, this would also allow us to ensure the various implementations of the same module are kept in alignment- simply import the module in a test module, attempt to pass it to a function that uses the corresponding protocol as an argument, and run pytype on it. In practice, this doesn't work (yet). PyCharm (erroneously) flags imported modules being passed where a protocol class is used[2]. Pytype has problems the other way- it fails to detect when a module that doesn't adhere to the protocol is passed to a protocol argument. The good news is that mypy properly detects this case. The bad news is that mypy spews a bunch of other errors when importing even simple modules, like the various `bdiff` modules. Therefore I'm punting on the tests for now because the type info around a loaded module in PyCharm is a clear win by itself. [1] https://peps.python.org/pep-0544/#modules-as-implementations-of-protocols [2] https://youtrack.jetbrains.com/issue/PY-58679/Support-modules-implementing-protocols

File last commit:

r52755:607e94e0 default
r52826:f2832de2 default
Show More
mpatch.py
144 lines | 3.5 KiB | text/x-python | PythonLexer
Martin Geisler
pure Python implementation of mpatch.c
r7699 # mpatch.py - Python implementation of mpatch.c
#
Raphaël Gomès
contributor: change mentions of mpm to olivia...
r47575 # Copyright 2009 Olivia Mackall <olivia@selenic.com> and others
Martin Geisler
pure Python implementation of mpatch.c
r7699 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Martin Geisler
pure Python implementation of mpatch.c
r7699
Matt Harbison
format: add many "missing" comma...
r52755 from __future__ import annotations
Gregory Szorc
mpatch: use absolute_import...
r27337
Gregory Szorc
py3: use io.BytesIO directly...
r49728 import io
Martin Geisler
pure/mpatch: use StringIO instead of mmap (issue1493)...
r7775 import struct
Gregory Szorc
mpatch: use absolute_import...
r27337
Matt Harbison
typing: add type hints to mpatch implementations...
r50494 from typing import (
List,
Tuple,
)
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use io.BytesIO directly...
r49728 stringio = io.BytesIO
Martin Geisler
pure Python implementation of mpatch.c
r7699
Augie Fackler
formatting: blacken the codebase...
r43346
timeless
mpatch: unify mpatchError (issue5182)...
r28782 class mpatchError(Exception):
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 """error raised when a delta cannot be decoded"""
timeless
mpatch: unify mpatchError (issue5182)...
r28782
Augie Fackler
formatting: blacken the codebase...
r43346
Martin Geisler
pure Python implementation of mpatch.c
r7699 # This attempts to apply a series of patches in time proportional to
# the total size of the patches, rather than patches * len(text). This
# means rather than shuffling strings around, we shuffle around
# pointers to fragments with fragment lists.
#
# When the fragment lists get too long, we collapse them. To do this
# efficiently, we do all our operations inside a buffer created by
# mmap and simply use memmove. This avoids creating a bunch of large
# temporary string buffers.
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: add type hints to mpatch implementations...
r50494 def _pull(
dst: List[Tuple[int, int]], src: List[Tuple[int, int]], l: int
) -> None: # pull l bytes from src
Augie Fackler
mpatch: move pull() method to top level...
r28587 while l:
f = src.pop()
Augie Fackler
formatting: blacken the codebase...
r43346 if f[0] > l: # do we need to split?
Augie Fackler
mpatch: move pull() method to top level...
r28587 src.append((f[0] - l, f[1] + l))
dst.append((l, f[1]))
return
dst.append(f)
l -= f[0]
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: add type hints to mpatch implementations...
r50494 def _move(m: stringio, dest: int, src: int, count: int) -> None:
Augie Fackler
mpatch: un-nest the move() method...
r28588 """move count bytes from src to dest
The file pointer is left at the end of dest.
"""
m.seek(src)
buf = m.read(count)
m.seek(dest)
m.write(buf)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: add type hints to mpatch implementations...
r50494 def _collect(
m: stringio, buf: int, list: List[Tuple[int, int]]
) -> Tuple[int, int]:
Augie Fackler
mpatch: move collect() to module level...
r28589 start = buf
for l, p in reversed(list):
_move(m, buf, p, l)
buf += l
return (buf - start, start)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: add type hints to mpatch implementations...
r50494 def patches(a: bytes, bins: List[bytes]) -> bytes:
Matt Mackall
many, many trivial check-code fixups
r10282 if not bins:
return a
Martin Geisler
pure Python implementation of mpatch.c
r7699
plens = [len(x) for x in bins]
pl = sum(plens)
bl = len(a) + pl
Augie Fackler
formatting: blacken the codebase...
r43346 tl = bl + bl + pl # enough for the patches and two working texts
Martin Geisler
pure Python implementation of mpatch.c
r7699 b1, b2 = 0, bl
Matt Mackall
many, many trivial check-code fixups
r10282 if not tl:
return a
Martin Geisler
pure Python implementation of mpatch.c
r7699
timeless
pycompat: switch to util.stringio for py3 compat
r28861 m = stringio()
Martin Geisler
pure Python implementation of mpatch.c
r7699
# load our original text
m.write(a)
frags = [(len(a), b1)]
# copy all the patches into our segment so we can memmove from them
pos = b2 + bl
m.seek(pos)
Alex Gaynor
style: never put multiple statements on one line...
r34436 for p in bins:
m.write(p)
Martin Geisler
pure Python implementation of mpatch.c
r7699
for plen in plens:
# if our list gets too long, execute it
if len(frags) > 128:
b2, b1 = b1, b2
Augie Fackler
mpatch: move collect() to module level...
r28589 frags = [_collect(m, b1, frags)]
Martin Geisler
pure Python implementation of mpatch.c
r7699
new = []
end = pos + plen
last = 0
while pos < end:
Martin Geisler
pure/mpatch: use StringIO instead of mmap (issue1493)...
r7775 m.seek(pos)
timeless
mpatch: unify mpatchError (issue5182)...
r28782 try:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 p1, p2, l = struct.unpack(b">lll", m.read(12))
timeless
mpatch: unify mpatchError (issue5182)...
r28782 except struct.error:
Matt Harbison
httppeer: avoid another bad reference before assignment warning...
r52633 raise mpatchError("patch cannot be decoded")
Augie Fackler
formatting: blacken the codebase...
r43346 _pull(new, frags, p1 - last) # what didn't change
_pull([], frags, p2 - p1) # what got deleted
new.append((l, pos + 12)) # what got added
Martin Geisler
pure Python implementation of mpatch.c
r7699 pos += l + 12
last = p2
Augie Fackler
formatting: blacken the codebase...
r43346 frags.extend(reversed(new)) # what was left at the end
Martin Geisler
pure Python implementation of mpatch.c
r7699
Augie Fackler
mpatch: move collect() to module level...
r28589 t = _collect(m, b2, frags)
Martin Geisler
pure Python implementation of mpatch.c
r7699
Martin Geisler
pure/mpatch: use StringIO instead of mmap (issue1493)...
r7775 m.seek(t[1])
return m.read(t[0])
Martin Geisler
pure Python implementation of mpatch.c
r7699
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Harbison
typing: add type hints to mpatch implementations...
r50494 def patchedsize(orig: int, delta: bytes) -> int:
Martin Geisler
pure Python implementation of mpatch.c
r7699 outlen, last, bin = 0, 0, 0
binend = len(delta)
data = 12
while data <= binend:
Augie Fackler
formatting: blacken the codebase...
r43346 decode = delta[bin : bin + 12]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 start, end, length = struct.unpack(b">lll", decode)
Martin Geisler
pure Python implementation of mpatch.c
r7699 if start > end:
break
bin = data + length
data = bin + 12
outlen += start - last
last = end
outlen += length
if bin != binend:
Matt Harbison
httppeer: avoid another bad reference before assignment warning...
r52633 raise mpatchError("patch cannot be decoded")
Martin Geisler
pure Python implementation of mpatch.c
r7699
outlen += orig - last
return outlen