##// END OF EJS Templates
pytype: ignore some signature mismatch in configitems...
marmoute -
r52185:9c5bd485 default
parent child Browse files
Show More
@@ -1,214 +1,214 b''
1 # configitems.py - centralized declaration of configuration option
1 # configitems.py - centralized declaration of configuration option
2 #
2 #
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8
8
9 import functools
9 import functools
10 import re
10 import re
11
11
12 from .utils import resourceutil
12 from .utils import resourceutil
13
13
14 from . import (
14 from . import (
15 encoding,
15 encoding,
16 error,
16 error,
17 )
17 )
18
18
19 try:
19 try:
20 import tomllib # pytype: disable=import-error
20 import tomllib # pytype: disable=import-error
21
21
22 tomllib.load # trigger lazy import
22 tomllib.load # trigger lazy import
23 except ModuleNotFoundError:
23 except ModuleNotFoundError:
24 # Python <3.11 compat
24 # Python <3.11 compat
25 from .thirdparty import tomli as tomllib
25 from .thirdparty import tomli as tomllib
26
26
27
27
28 def loadconfigtable(ui, extname, configtable):
28 def loadconfigtable(ui, extname, configtable):
29 """update config item known to the ui with the extension ones"""
29 """update config item known to the ui with the extension ones"""
30 for section, items in sorted(configtable.items()):
30 for section, items in sorted(configtable.items()):
31 knownitems = ui._knownconfig.setdefault(section, itemregister())
31 knownitems = ui._knownconfig.setdefault(section, itemregister())
32 knownkeys = set(knownitems)
32 knownkeys = set(knownitems)
33 newkeys = set(items)
33 newkeys = set(items)
34 for key in sorted(knownkeys & newkeys):
34 for key in sorted(knownkeys & newkeys):
35 msg = b"extension '%s' overwrites config item '%s.%s'"
35 msg = b"extension '%s' overwrites config item '%s.%s'"
36 msg %= (extname, section, key)
36 msg %= (extname, section, key)
37 ui.develwarn(msg, config=b'warn-config')
37 ui.develwarn(msg, config=b'warn-config')
38
38
39 knownitems.update(items)
39 knownitems.update(items)
40
40
41
41
42 class configitem:
42 class configitem:
43 """represent a known config item
43 """represent a known config item
44
44
45 :section: the official config section where to find this item,
45 :section: the official config section where to find this item,
46 :name: the official name within the section,
46 :name: the official name within the section,
47 :default: default value for this item,
47 :default: default value for this item,
48 :alias: optional list of tuples as alternatives,
48 :alias: optional list of tuples as alternatives,
49 :generic: this is a generic definition, match name using regular expression.
49 :generic: this is a generic definition, match name using regular expression.
50 """
50 """
51
51
52 def __init__(
52 def __init__(
53 self,
53 self,
54 section,
54 section,
55 name,
55 name,
56 default=None,
56 default=None,
57 alias=(),
57 alias=(),
58 generic=False,
58 generic=False,
59 priority=0,
59 priority=0,
60 experimental=False,
60 experimental=False,
61 documentation="",
61 documentation="",
62 in_core_extension=None,
62 in_core_extension=None,
63 ):
63 ):
64 self.section = section
64 self.section = section
65 self.name = name
65 self.name = name
66 self.default = default
66 self.default = default
67 self.documentation = documentation
67 self.documentation = documentation
68 self.alias = list(alias)
68 self.alias = list(alias)
69 self.generic = generic
69 self.generic = generic
70 self.priority = priority
70 self.priority = priority
71 self.experimental = experimental
71 self.experimental = experimental
72 self._re = None
72 self._re = None
73 self.in_core_extension = in_core_extension
73 self.in_core_extension = in_core_extension
74 if generic:
74 if generic:
75 self._re = re.compile(self.name)
75 self._re = re.compile(self.name)
76
76
77
77
78 class itemregister(dict):
78 class itemregister(dict):
79 """A specialized dictionary that can handle wild-card selection"""
79 """A specialized dictionary that can handle wild-card selection"""
80
80
81 def __init__(self):
81 def __init__(self):
82 super(itemregister, self).__init__()
82 super(itemregister, self).__init__()
83 self._generics = set()
83 self._generics = set()
84
84
85 def update(self, other):
85 def update(self, other): # pytype: disable=signature-mismatch
86 super(itemregister, self).update(other)
86 super(itemregister, self).update(other)
87 self._generics.update(other._generics)
87 self._generics.update(other._generics)
88
88
89 def __setitem__(self, key, item):
89 def __setitem__(self, key, item):
90 super(itemregister, self).__setitem__(key, item)
90 super(itemregister, self).__setitem__(key, item)
91 if item.generic:
91 if item.generic:
92 self._generics.add(item)
92 self._generics.add(item)
93
93
94 def get(self, key):
94 def get(self, key):
95 baseitem = super(itemregister, self).get(key)
95 baseitem = super(itemregister, self).get(key)
96 if baseitem is not None and not baseitem.generic:
96 if baseitem is not None and not baseitem.generic:
97 return baseitem
97 return baseitem
98
98
99 # search for a matching generic item
99 # search for a matching generic item
100 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
100 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
101 for item in generics:
101 for item in generics:
102 # we use 'match' instead of 'search' to make the matching simpler
102 # we use 'match' instead of 'search' to make the matching simpler
103 # for people unfamiliar with regular expression. Having the match
103 # for people unfamiliar with regular expression. Having the match
104 # rooted to the start of the string will produce less surprising
104 # rooted to the start of the string will produce less surprising
105 # result for user writing simple regex for sub-attribute.
105 # result for user writing simple regex for sub-attribute.
106 #
106 #
107 # For example using "color\..*" match produces an unsurprising
107 # For example using "color\..*" match produces an unsurprising
108 # result, while using search could suddenly match apparently
108 # result, while using search could suddenly match apparently
109 # unrelated configuration that happens to contains "color."
109 # unrelated configuration that happens to contains "color."
110 # anywhere. This is a tradeoff where we favor requiring ".*" on
110 # anywhere. This is a tradeoff where we favor requiring ".*" on
111 # some match to avoid the need to prefix most pattern with "^".
111 # some match to avoid the need to prefix most pattern with "^".
112 # The "^" seems more error prone.
112 # The "^" seems more error prone.
113 if item._re.match(key):
113 if item._re.match(key):
114 return item
114 return item
115
115
116 return None
116 return None
117
117
118
118
119 def sanitize_item(item):
119 def sanitize_item(item):
120 """Apply the transformations that are encoded on top of the pure data"""
120 """Apply the transformations that are encoded on top of the pure data"""
121
121
122 # Set the special defaults
122 # Set the special defaults
123 default_type_key = "default-type"
123 default_type_key = "default-type"
124 default_type = item.pop(default_type_key, None)
124 default_type = item.pop(default_type_key, None)
125 if default_type == "dynamic":
125 if default_type == "dynamic":
126 item["default"] = dynamicdefault
126 item["default"] = dynamicdefault
127 elif default_type == "list_type":
127 elif default_type == "list_type":
128 item["default"] = list
128 item["default"] = list
129 elif default_type == "lambda":
129 elif default_type == "lambda":
130 assert isinstance(item["default"], list)
130 assert isinstance(item["default"], list)
131 default = [e.encode() for e in item["default"]]
131 default = [e.encode() for e in item["default"]]
132 item["default"] = lambda: default
132 item["default"] = lambda: default
133 elif default_type == "lazy_module":
133 elif default_type == "lazy_module":
134 item["default"] = lambda: encoding.encoding
134 item["default"] = lambda: encoding.encoding
135 else:
135 else:
136 if default_type is not None:
136 if default_type is not None:
137 msg = "invalid default config type %r for '%s.%s'"
137 msg = "invalid default config type %r for '%s.%s'"
138 msg %= (default_type, item["section"], item["name"])
138 msg %= (default_type, item["section"], item["name"])
139 raise error.ProgrammingError(msg)
139 raise error.ProgrammingError(msg)
140
140
141 # config expects bytes
141 # config expects bytes
142 alias = item.get("alias")
142 alias = item.get("alias")
143 if alias:
143 if alias:
144 item["alias"] = [(k.encode(), v.encode()) for (k, v) in alias]
144 item["alias"] = [(k.encode(), v.encode()) for (k, v) in alias]
145 if isinstance(item.get("default"), str):
145 if isinstance(item.get("default"), str):
146 item["default"] = item["default"].encode()
146 item["default"] = item["default"].encode()
147 item["section"] = item["section"].encode()
147 item["section"] = item["section"].encode()
148 item["name"] = item["name"].encode()
148 item["name"] = item["name"].encode()
149
149
150
150
151 def read_configitems_file():
151 def read_configitems_file():
152 """Returns the deserialized TOML structure from the configitems file"""
152 """Returns the deserialized TOML structure from the configitems file"""
153 with resourceutil.open_resource(b"mercurial", b"configitems.toml") as fp:
153 with resourceutil.open_resource(b"mercurial", b"configitems.toml") as fp:
154 return tomllib.load(fp)
154 return tomllib.load(fp)
155
155
156
156
157 def configitems_from_toml(items):
157 def configitems_from_toml(items):
158 """Register the configitems from the *deserialized* toml file"""
158 """Register the configitems from the *deserialized* toml file"""
159 for item in items["items"]:
159 for item in items["items"]:
160 sanitize_item(item)
160 sanitize_item(item)
161 coreconfigitem(**item)
161 coreconfigitem(**item)
162
162
163 templates = items["templates"]
163 templates = items["templates"]
164
164
165 for application in items["template-applications"]:
165 for application in items["template-applications"]:
166 template_items = templates[application["template"]]
166 template_items = templates[application["template"]]
167
167
168 for template_item in template_items:
168 for template_item in template_items:
169 item = template_item.copy()
169 item = template_item.copy()
170 prefix = application.get("prefix", "")
170 prefix = application.get("prefix", "")
171 item["section"] = application["section"]
171 item["section"] = application["section"]
172 if prefix:
172 if prefix:
173 item["name"] = f'{prefix}.{item["suffix"]}'
173 item["name"] = f'{prefix}.{item["suffix"]}'
174 else:
174 else:
175 item["name"] = item["suffix"]
175 item["name"] = item["suffix"]
176
176
177 sanitize_item(item)
177 sanitize_item(item)
178 item.pop("suffix", None)
178 item.pop("suffix", None)
179 coreconfigitem(**item)
179 coreconfigitem(**item)
180
180
181
181
182 def import_configitems_from_file():
182 def import_configitems_from_file():
183 as_toml = read_configitems_file()
183 as_toml = read_configitems_file()
184 configitems_from_toml(as_toml)
184 configitems_from_toml(as_toml)
185
185
186
186
187 coreitems = {}
187 coreitems = {}
188
188
189
189
190 def _register(configtable, *args, **kwargs):
190 def _register(configtable, *args, **kwargs):
191 item = configitem(*args, **kwargs)
191 item = configitem(*args, **kwargs)
192 section = configtable.setdefault(item.section, itemregister())
192 section = configtable.setdefault(item.section, itemregister())
193 if item.name in section:
193 if item.name in section:
194 msg = b"duplicated config item registration for '%s.%s'"
194 msg = b"duplicated config item registration for '%s.%s'"
195 raise error.ProgrammingError(msg % (item.section, item.name))
195 raise error.ProgrammingError(msg % (item.section, item.name))
196 section[item.name] = item
196 section[item.name] = item
197
197
198
198
199 # special value for case where the default is derived from other values
199 # special value for case where the default is derived from other values
200 dynamicdefault = object()
200 dynamicdefault = object()
201
201
202 # Registering actual config items
202 # Registering actual config items
203
203
204
204
205 def getitemregister(configtable):
205 def getitemregister(configtable):
206 f = functools.partial(_register, configtable)
206 f = functools.partial(_register, configtable)
207 # export pseudo enum as configitem.*
207 # export pseudo enum as configitem.*
208 f.dynamicdefault = dynamicdefault
208 f.dynamicdefault = dynamicdefault
209 return f
209 return f
210
210
211
211
212 coreconfigitem = getitemregister(coreitems)
212 coreconfigitem = getitemregister(coreitems)
213
213
214 import_configitems_from_file()
214 import_configitems_from_file()
General Comments 0
You need to be logged in to leave comments. Login now