pointer.py
88 lines
| 2.8 KiB
| text/x-python
|
PythonLexer
Matt Harbison
|
r35097 | # pointer.py - Git-LFS pointer serialization | ||
# | ||||
# Copyright 2017 Facebook, Inc. | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
import re | ||||
Matt Harbison
|
r35098 | from mercurial.i18n import _ | ||
Matt Harbison
|
r35097 | from mercurial import ( | ||
error, | ||||
Augie Fackler
|
r36620 | pycompat, | ||
Matt Harbison
|
r35097 | ) | ||
Augie Fackler
|
r43346 | from mercurial.utils import stringutil | ||
Matt Harbison
|
r35097 | |||
Gregory Szorc
|
r39813 | class InvalidPointer(error.StorageError): | ||
Matt Harbison
|
r35097 | pass | ||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r35097 | class gitlfspointer(dict): | ||
Augie Fackler
|
r43347 | VERSION = b'https://git-lfs.github.com/spec/v1' | ||
Matt Harbison
|
r35097 | |||
def __init__(self, *args, **kwargs): | ||||
Augie Fackler
|
r43347 | self[b'version'] = self.VERSION | ||
Augie Fackler
|
r36620 | super(gitlfspointer, self).__init__(*args) | ||
self.update(pycompat.byteskwargs(kwargs)) | ||||
Matt Harbison
|
r35097 | |||
@classmethod | ||||
def deserialize(cls, text): | ||||
try: | ||||
Augie Fackler
|
r43347 | return cls(l.split(b' ', 1) for l in text.splitlines()).validate() | ||
Augie Fackler
|
r43346 | except ValueError: # l.split returns 1 item instead of 2 | ||
raise InvalidPointer( | ||||
Augie Fackler
|
r43347 | _(b'cannot parse git-lfs text: %s') % stringutil.pprint(text) | ||
Augie Fackler
|
r43346 | ) | ||
Matt Harbison
|
r35097 | |||
def serialize(self): | ||||
Augie Fackler
|
r43347 | sortkeyfunc = lambda x: (x[0] != b'version', x) | ||
Gregory Szorc
|
r49772 | items = sorted(self.validate().items(), key=sortkeyfunc) | ||
Augie Fackler
|
r43347 | return b''.join(b'%s %s\n' % (k, v) for k, v in items) | ||
Matt Harbison
|
r35097 | |||
def oid(self): | ||||
Augie Fackler
|
r43347 | return self[b'oid'].split(b':')[-1] | ||
Matt Harbison
|
r35097 | |||
def size(self): | ||||
Augie Fackler
|
r43347 | return int(self[b'size']) | ||
Matt Harbison
|
r35097 | |||
# regular expressions used by _validate | ||||
# see https://github.com/git-lfs/git-lfs/blob/master/docs/spec.md | ||||
Augie Fackler
|
r36618 | _keyre = re.compile(br'\A[a-z0-9.-]+\Z') | ||
_valuere = re.compile(br'\A[^\n]*\Z') | ||||
Matt Harbison
|
r35097 | _requiredre = { | ||
Augie Fackler
|
r43347 | b'size': re.compile(br'\A[0-9]+\Z'), | ||
b'oid': re.compile(br'\Asha256:[0-9a-f]{64}\Z'), | ||||
b'version': re.compile(br'\A%s\Z' % stringutil.reescape(VERSION)), | ||||
Matt Harbison
|
r35097 | } | ||
def validate(self): | ||||
"""raise InvalidPointer on error. return self if there is no error""" | ||||
requiredcount = 0 | ||||
Gregory Szorc
|
r49768 | for k, v in self.items(): | ||
Matt Harbison
|
r35097 | if k in self._requiredre: | ||
if not self._requiredre[k].match(v): | ||||
Matt Harbison
|
r38178 | raise InvalidPointer( | ||
Augie Fackler
|
r43347 | _(b'unexpected lfs pointer value: %s=%s') | ||
Augie Fackler
|
r43346 | % (k, stringutil.pprint(v)) | ||
) | ||||
Matt Harbison
|
r35097 | requiredcount += 1 | ||
elif not self._keyre.match(k): | ||||
Augie Fackler
|
r43347 | raise InvalidPointer(_(b'unexpected lfs pointer key: %s') % k) | ||
Matt Harbison
|
r35097 | if not self._valuere.match(v): | ||
Augie Fackler
|
r43346 | raise InvalidPointer( | ||
Augie Fackler
|
r43347 | _(b'unexpected lfs pointer value: %s=%s') | ||
Augie Fackler
|
r43346 | % (k, stringutil.pprint(v)) | ||
) | ||||
Matt Harbison
|
r35097 | if len(self._requiredre) != requiredcount: | ||
miss = sorted(set(self._requiredre.keys()).difference(self.keys())) | ||||
Augie Fackler
|
r43346 | raise InvalidPointer( | ||
Augie Fackler
|
r43347 | _(b'missing lfs pointer keys: %s') % b', '.join(miss) | ||
Augie Fackler
|
r43346 | ) | ||
Matt Harbison
|
r35097 | return self | ||
Augie Fackler
|
r43346 | |||
Matt Harbison
|
r35097 | deserialize = gitlfspointer.deserialize | ||