##// END OF EJS Templates
config: gather constant and type into the `__init__.py`...
marmoute -
r53322:3e79ca01 default
parent child Browse files
Show More
@@ -0,0 +1,36
1 # configuration related constants
2
3 from __future__ import annotations
4
5 from typing import (
6 List,
7 Tuple,
8 Union,
9 )
10
11 # keep typing simple for now
12 ConfigLevelT = str
13 LEVEL_USER = 'user' # "user" is the default level and never passed explicitly
14 LEVEL_LOCAL = 'local'
15 LEVEL_GLOBAL = 'global'
16 LEVEL_SHARED = 'shared'
17 LEVEL_NON_SHARED = 'non_shared'
18 EDIT_LEVELS = (
19 LEVEL_USER,
20 LEVEL_LOCAL,
21 LEVEL_GLOBAL,
22 LEVEL_SHARED,
23 LEVEL_NON_SHARED,
24 )
25
26 ConfigItemT = Tuple[bytes, bytes, bytes, bytes]
27 ResourceIDT = Tuple[bytes, bytes]
28 FileRCT = bytes
29 ComponentT = Tuple[
30 bytes,
31 Union[
32 List[ConfigItemT],
33 FileRCT,
34 ResourceIDT,
35 ],
36 ]
@@ -1,201 +1,196
1 1 # Gather code related to command dealing with configuration.
2 2
3 3 from __future__ import annotations
4 4
5 5 import os
6 6
7 7 from typing import Any, Collection, Dict, Optional
8 8
9 9 from ..i18n import _
10 10
11 11 from .. import (
12 12 cmdutil,
13 13 error,
14 14 formatter,
15 15 pycompat,
16 16 requirements,
17 17 ui as uimod,
18 18 util,
19 19 vfs as vfsmod,
20 20 )
21 21
22 from . import rcutil
22 from . import (
23 ConfigLevelT,
24 EDIT_LEVELS,
25 LEVEL_GLOBAL,
26 LEVEL_LOCAL,
27 LEVEL_NON_SHARED,
28 LEVEL_SHARED,
29 LEVEL_USER,
30 rcutil,
31 )
23 32
24 33 EDIT_FLAG = 'edit'
25 34
26 35
27 # keep typing simple for now
28 ConfigLevelT = str
29 LEVEL_USER = 'user' # "user" is the default level and never passed explicitly
30 LEVEL_LOCAL = 'local'
31 LEVEL_GLOBAL = 'global'
32 LEVEL_SHARED = 'shared'
33 LEVEL_NON_SHARED = 'non_shared'
34 EDIT_LEVELS = (
35 LEVEL_USER,
36 LEVEL_LOCAL,
37 LEVEL_GLOBAL,
38 LEVEL_SHARED,
39 LEVEL_NON_SHARED,
40 )
41
42
43 36 def find_edit_level(
44 ui: uimod.ui, repo, opts: Dict[str, Any]
37 ui: uimod.ui,
38 repo,
39 opts: Dict[str, Any],
45 40 ) -> Optional[ConfigLevelT]:
46 41 """return the level we should edit, if any.
47 42
48 43 Parse the command option to detect when an edit is requested, and if so the
49 44 configuration level we should edit.
50 45 """
51 46 if opts.get(EDIT_FLAG) or any(opts.get(o) for o in EDIT_LEVELS):
52 47 cmdutil.check_at_most_one_arg(opts, *EDIT_LEVELS)
53 48 for level in EDIT_LEVELS:
54 49 if opts.get(level):
55 50 return level
56 51 return EDIT_LEVELS[0]
57 52 return None
58 53
59 54
60 55 def edit_config(ui: uimod.ui, repo, level: ConfigLevelT) -> None:
61 56 """let the user edit configuration file for the given level"""
62 57
63 58 if level == LEVEL_USER:
64 59 paths = rcutil.userrcpath()
65 60 elif level == LEVEL_GLOBAL:
66 61 paths = rcutil.systemrcpath()
67 62 elif level == LEVEL_LOCAL:
68 63 if not repo:
69 64 raise error.InputError(_(b"can't use --local outside a repository"))
70 65 paths = [repo.vfs.join(b'hgrc')]
71 66 elif level == LEVEL_NON_SHARED:
72 67 paths = [repo.vfs.join(b'hgrc-not-shared')]
73 68 elif level == LEVEL_SHARED:
74 69 if not repo.shared():
75 70 raise error.InputError(
76 71 _(b"repository is not shared; can't use --shared")
77 72 )
78 73 if requirements.SHARESAFE_REQUIREMENT not in repo.requirements:
79 74 raise error.InputError(
80 75 _(
81 76 b"share safe feature not enabled; "
82 77 b"unable to edit shared source repository config"
83 78 )
84 79 )
85 80 paths = [vfsmod.vfs(repo.sharedpath).join(b'hgrc')]
86 81 else:
87 82 msg = 'unknown config level: %s' % level
88 83 raise error.ProgrammingError(msg)
89 84
90 85 for f in paths:
91 86 if os.path.exists(f):
92 87 break
93 88 else:
94 89 if LEVEL_GLOBAL:
95 90 samplehgrc = uimod.samplehgrcs[b'global']
96 91 elif LEVEL_LOCAL:
97 92 samplehgrc = uimod.samplehgrcs[b'local']
98 93 else:
99 94 samplehgrc = uimod.samplehgrcs[b'user']
100 95
101 96 f = paths[0]
102 97 util.writefile(f, util.tonativeeol(samplehgrc))
103 98
104 99 editor = ui.geteditor()
105 100 ui.system(
106 101 b"%s \"%s\"" % (editor, f),
107 102 onerr=error.InputError,
108 103 errprefix=_(b"edit failed"),
109 104 blockedtag=b'config_edit',
110 105 )
111 106
112 107
113 108 def show_component(ui: uimod.ui, repo) -> None:
114 109 """show the component used to build the config
115 110
116 111 XXX this skip over various source and ignore the repository config, so it
117 112 XXX is probably useless old code.
118 113 """
119 114 for t, f in rcutil.rccomponents():
120 115 if t == b'path':
121 116 ui.debug(b'read config from: %s\n' % f)
122 117 elif t == b'resource':
123 118 ui.debug(b'read config from: resource:%s.%s\n' % (f[0], f[1]))
124 119 elif t == b'items':
125 120 # Don't print anything for 'items'.
126 121 pass
127 122 else:
128 123 raise error.ProgrammingError(b'unknown rctype: %s' % t)
129 124
130 125
131 126 def show_config(
132 127 ui: uimod.ui,
133 128 repo,
134 129 value_filters: Collection[bytes],
135 130 formatter_options: dict,
136 131 untrusted: bool = False,
137 132 all_known: bool = False,
138 133 show_source: bool = False,
139 134 ) -> bool:
140 135 """Display config value to the user
141 136
142 137 The display is done using a dedicated `formatter` object.
143 138
144 139
145 140 :value_filters:
146 141 if non-empty filter the display value according to these filters. If
147 142 the filter does not match any value, the function return False. True
148 143 otherwise.
149 144
150 145 :formatter_option:
151 146 options passed to the formatter
152 147
153 148 :untrusted:
154 149 When set, use untrusted value instead of ignoring them
155 150
156 151 :all_known:
157 152 Display all known config item, not just the one with an explicit value.
158 153
159 154 :show_source:
160 155 Show where each value has been defined.
161 156 """
162 157 fm = ui.formatter(b'config', formatter_options)
163 158 selsections = selentries = []
164 159 filtered = False
165 160 if value_filters:
166 161 selsections = [v for v in value_filters if b'.' not in v]
167 162 selentries = [v for v in value_filters if b'.' in v]
168 163 filtered = True
169 164 uniquesel = len(selentries) == 1 and not selsections
170 165 selsections = set(selsections)
171 166 selentries = set(selentries)
172 167
173 168 matched = False
174 169 entries = ui.walkconfig(untrusted=untrusted, all_known=all_known)
175 170 for section, name, value in entries:
176 171 source = ui.configsource(section, name, untrusted)
177 172 value = pycompat.bytestr(value)
178 173 defaultvalue = ui.configdefault(section, name)
179 174 if fm.isplain():
180 175 source = source or b'none'
181 176 value = value.replace(b'\n', b'\\n')
182 177 entryname = section + b'.' + name
183 178 if filtered and not (section in selsections or entryname in selentries):
184 179 continue
185 180 fm.startitem()
186 181 fm.condwrite(show_source, b'source', b'%s: ', source)
187 182 if uniquesel:
188 183 fm.data(name=entryname)
189 184 fm.write(b'value', b'%s\n', value)
190 185 else:
191 186 fm.write(b'name value', b'%s=%s\n', entryname, value)
192 187 if formatter.isprintable(defaultvalue):
193 188 fm.data(defaultvalue=defaultvalue)
194 189 elif isinstance(defaultvalue, list) and all(
195 190 formatter.isprintable(e) for e in defaultvalue
196 191 ):
197 192 fm.data(defaultvalue=fm.formatlist(defaultvalue, name=b'value'))
198 193 # TODO: no idea how to process unsupported defaultvalue types
199 194 matched = True
200 195 fm.end()
201 196 return matched
@@ -1,173 +1,165
1 1 # rcutil.py - utilities about config paths, special config sections etc.
2 2 #
3 3 # Copyright Mercurial Contributors
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import annotations
9 9
10 10 import os
11 11
12 12 from typing import (
13 13 Dict,
14 14 List,
15 15 Optional,
16 Tuple,
17 Union,
18 16 )
19 17
20 18 from .. import (
19 configuration as conf_mod,
21 20 encoding,
22 21 localrepo,
23 22 pycompat,
24 23 requirements as requirementsmod,
25 24 util,
26 25 vfs,
27 26 )
28 27
29 28 from ..utils import resourceutil
30 29
31 30 if pycompat.iswindows:
32 31 from .. import scmwindows as scmplatform
33 32 else:
34 33 from .. import scmposix as scmplatform
35 34
36 35 fallbackpager = scmplatform.fallbackpager
37 36 systemrcpath = scmplatform.systemrcpath
38 37 userrcpath = scmplatform.userrcpath
39 38
40 ConfigItemT = Tuple[bytes, bytes, bytes, bytes]
41 ResourceIDT = Tuple[bytes, bytes]
42 FileRCT = bytes
43 ComponentT = Tuple[
44 bytes,
45 Union[
46 List[ConfigItemT],
47 FileRCT,
48 ResourceIDT,
49 ],
50 ]
39 ComponentT = conf_mod.ComponentT
40 ConfigItemT = conf_mod.ConfigItemT
41 FileRCT = conf_mod.FileRCT
42 ResourceIDT = conf_mod.ResourceIDT
51 43
52 44
53 45 def _expandrcpath(path: bytes) -> List[FileRCT]:
54 46 '''path could be a file or a directory. return a list of file paths'''
55 47 p = util.expandpath(path)
56 48 if os.path.isdir(p):
57 49 join = os.path.join
58 50 return sorted(
59 51 join(p, f) for f, k in util.listdir(p) if f.endswith(b'.rc')
60 52 )
61 53 return [p]
62 54
63 55
64 56 def envrcitems(env: Optional[Dict[bytes, bytes]] = None) -> List[ConfigItemT]:
65 57 """Return [(section, name, value, source)] config items.
66 58
67 59 The config items are extracted from environment variables specified by env,
68 60 used to override systemrc, but not userrc.
69 61
70 62 If env is not provided, encoding.environ will be used.
71 63 """
72 64 if env is None:
73 65 env = encoding.environ
74 66 checklist = [
75 67 (b'EDITOR', b'ui', b'editor'),
76 68 (b'VISUAL', b'ui', b'editor'),
77 69 (b'PAGER', b'pager', b'pager'),
78 70 ]
79 71 result = []
80 72 for envname, section, configname in checklist:
81 73 if envname not in env:
82 74 continue
83 75 result.append((section, configname, env[envname], b'$%s' % envname))
84 76 return result
85 77
86 78
87 79 def default_rc_resources() -> List[ResourceIDT]:
88 80 """return rc resource IDs in defaultrc"""
89 81 rsrcs = resourceutil.contents(b'mercurial.defaultrc')
90 82 return [
91 83 (b'mercurial.defaultrc', r)
92 84 for r in sorted(rsrcs)
93 85 if resourceutil.is_resource(b'mercurial.defaultrc', r)
94 86 and r.endswith(b'.rc')
95 87 ]
96 88
97 89
98 90 def rccomponents() -> List[ComponentT]:
99 91 """return an ordered [(type, obj)] about where to load configs.
100 92
101 93 respect $HGRCPATH. if $HGRCPATH is empty, only .hg/hgrc of current repo is
102 94 used. if $HGRCPATH is not set, the platform default will be used.
103 95
104 96 if a directory is provided, *.rc files under it will be used.
105 97
106 98 type could be either 'path', 'items' or 'resource'. If type is 'path',
107 99 obj is a string, and is the config file path. if type is 'items', obj is a
108 100 list of (section, name, value, source) that should fill the config directly.
109 101 If type is 'resource', obj is a tuple of (package name, resource name).
110 102 """
111 103 envrc = (b'items', envrcitems())
112 104
113 105 if b'HGRCPATH' in encoding.environ:
114 106 # assume HGRCPATH is all about user configs so environments can be
115 107 # overridden.
116 108 _rccomponents = [envrc]
117 109 for p in encoding.environ[b'HGRCPATH'].split(pycompat.ospathsep):
118 110 if not p:
119 111 continue
120 112 _rccomponents.extend((b'path', p) for p in _expandrcpath(p))
121 113 else:
122 114 _rccomponents = [(b'resource', r) for r in default_rc_resources()]
123 115
124 116 normpaths = lambda paths: [
125 117 (b'path', os.path.normpath(p)) for p in paths
126 118 ]
127 119 _rccomponents.extend(normpaths(systemrcpath()))
128 120 _rccomponents.append(envrc)
129 121 _rccomponents.extend(normpaths(userrcpath()))
130 122 return _rccomponents
131 123
132 124
133 125 def _shared_source_component(path: bytes) -> List[FileRCT]:
134 126 """if the current repository is shared one, this tries to read
135 127 .hg/hgrc of shared source if we are in share-safe mode
136 128
137 129 This should be called before reading .hg/hgrc or the main repo
138 130 as that overrides config set in shared source"""
139 131 try:
140 132 with open(os.path.join(path, b".hg", b"requires"), "rb") as fp:
141 133 requirements = set(fp.read().splitlines())
142 134 if not (
143 135 requirementsmod.SHARESAFE_REQUIREMENT in requirements
144 136 and requirementsmod.SHARED_REQUIREMENT in requirements
145 137 ):
146 138 return []
147 139 hgvfs = vfs.vfs(os.path.join(path, b".hg"))
148 140 sharedvfs = localrepo._getsharedvfs(hgvfs, requirements)
149 141 return [sharedvfs.join(b"hgrc")]
150 142 except IOError:
151 143 pass
152 144 return []
153 145
154 146
155 147 def repo_components(repo_path: bytes) -> List[ComponentT]:
156 148 """return the list of config file to read for a repository"""
157 149 components = []
158 150 components.extend(_shared_source_component(repo_path))
159 151 components.append(os.path.join(repo_path, b".hg", b"hgrc"))
160 152 components.append(os.path.join(repo_path, b".hg", b"hgrc-not-shared"))
161 153 return [(b'path', c) for c in components]
162 154
163 155
164 156 def defaultpagerenv() -> Dict[bytes, bytes]:
165 157 """return a dict of default environment variables and their values,
166 158 intended to be set before starting a pager.
167 159 """
168 160 return {b'LESS': b'FRX', b'LV': b'-c'}
169 161
170 162
171 163 def use_repo_hgrc() -> bool:
172 164 """True if repositories `.hg/hgrc` config should be read"""
173 165 return b'HGRCSKIPREPO' not in encoding.environ
General Comments 0
You need to be logged in to leave comments. Login now