comparison mercurial/scmutil.py @ 32270:218ca8526ec0

scmutil: make simplekeyvaluefile able to have a non-key-value first line To ease migration from files with version numbers in their first lines, we want simplekeyvaluefile to support a non-key-value first line. In this way, old versions of Mercurial will read such files, discover a newer version than the one they know how to handle and fail gracefully, rather than with exception. Shelve's shelvestate file is an example.
author Kostia Balytskyi <ikostia@fb.com>
date Thu, 11 May 2017 08:49:33 -0700
parents ed2c44741190
children 4bec8cce6a09
comparison
equal deleted inserted replaced
32269:ed2c44741190 32270:218ca8526ec0
915 class simplekeyvaluefile(object): 915 class simplekeyvaluefile(object):
916 """A simple file with key=value lines 916 """A simple file with key=value lines
917 917
918 Keys must be alphanumerics and start with a letter, values must not 918 Keys must be alphanumerics and start with a letter, values must not
919 contain '\n' characters""" 919 contain '\n' characters"""
920 firstlinekey = '__firstline'
920 921
921 def __init__(self, vfs, path, keys=None): 922 def __init__(self, vfs, path, keys=None):
922 self.vfs = vfs 923 self.vfs = vfs
923 self.path = path 924 self.path = path
924 925
925 def read(self): 926 def read(self, firstlinenonkeyval=False):
927 """Read the contents of a simple key-value file
928
929 'firstlinenonkeyval' indicates whether the first line of file should
930 be treated as a key-value pair or reuturned fully under the
931 __firstline key."""
926 lines = self.vfs.readlines(self.path) 932 lines = self.vfs.readlines(self.path)
933 d = {}
934 if firstlinenonkeyval:
935 if not lines:
936 e = _("empty simplekeyvalue file")
937 raise error.CorruptedState(e)
938 # we don't want to include '\n' in the __firstline
939 d[self.firstlinekey] = lines[0][:-1]
940 del lines[0]
941
927 try: 942 try:
928 # the 'if line.strip()' part prevents us from failing on empty 943 # the 'if line.strip()' part prevents us from failing on empty
929 # lines which only contain '\n' therefore are not skipped 944 # lines which only contain '\n' therefore are not skipped
930 # by 'if line' 945 # by 'if line'
931 d = dict(line[:-1].split('=', 1) for line in lines if line.strip()) 946 updatedict = dict(line[:-1].split('=', 1) for line in lines
947 if line.strip())
948 if self.firstlinekey in updatedict:
949 e = _("%r can't be used as a key")
950 raise error.CorruptedState(e % self.firstlinekey)
951 d.update(updatedict)
932 except ValueError as e: 952 except ValueError as e:
933 raise error.CorruptedState(str(e)) 953 raise error.CorruptedState(str(e))
934 return d 954 return d
935 955
936 def write(self, data): 956 def write(self, data, firstline=None):
937 """Write key=>value mapping to a file 957 """Write key=>value mapping to a file
938 data is a dict. Keys must be alphanumerical and start with a letter. 958 data is a dict. Keys must be alphanumerical and start with a letter.
939 Values must not contain newline characters.""" 959 Values must not contain newline characters.
960
961 If 'firstline' is not None, it is written to file before
962 everything else, as it is, not in a key=value form"""
940 lines = [] 963 lines = []
964 if firstline is not None:
965 lines.append('%s\n' % firstline)
966
941 for k, v in data.items(): 967 for k, v in data.items():
968 if k == self.firstlinekey:
969 e = "key name '%s' is reserved" % self.firstlinekey
970 raise error.ProgrammingError(e)
942 if not k[0].isalpha(): 971 if not k[0].isalpha():
943 e = "keys must start with a letter in a key-value file" 972 e = "keys must start with a letter in a key-value file"
944 raise error.ProgrammingError(e) 973 raise error.ProgrammingError(e)
945 if not k.isalnum(): 974 if not k.isalnum():
946 e = "invalid key name in a simple key-value file" 975 e = "invalid key name in a simple key-value file"