outerbounds 0.3.71__py3-none-any.whl → 0.3.75__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. outerbounds/_vendor/PyYAML.LICENSE +20 -0
  2. outerbounds/_vendor/__init__.py +0 -0
  3. outerbounds/_vendor/_yaml/__init__.py +34 -0
  4. outerbounds/_vendor/click/__init__.py +73 -0
  5. outerbounds/_vendor/click/_compat.py +626 -0
  6. outerbounds/_vendor/click/_termui_impl.py +717 -0
  7. outerbounds/_vendor/click/_textwrap.py +49 -0
  8. outerbounds/_vendor/click/_winconsole.py +279 -0
  9. outerbounds/_vendor/click/core.py +2998 -0
  10. outerbounds/_vendor/click/decorators.py +497 -0
  11. outerbounds/_vendor/click/exceptions.py +287 -0
  12. outerbounds/_vendor/click/formatting.py +301 -0
  13. outerbounds/_vendor/click/globals.py +68 -0
  14. outerbounds/_vendor/click/parser.py +529 -0
  15. outerbounds/_vendor/click/py.typed +0 -0
  16. outerbounds/_vendor/click/shell_completion.py +580 -0
  17. outerbounds/_vendor/click/termui.py +787 -0
  18. outerbounds/_vendor/click/testing.py +479 -0
  19. outerbounds/_vendor/click/types.py +1073 -0
  20. outerbounds/_vendor/click/utils.py +580 -0
  21. outerbounds/_vendor/click.LICENSE +28 -0
  22. outerbounds/_vendor/vendor_any.txt +2 -0
  23. outerbounds/_vendor/yaml/__init__.py +471 -0
  24. outerbounds/_vendor/yaml/composer.py +146 -0
  25. outerbounds/_vendor/yaml/constructor.py +862 -0
  26. outerbounds/_vendor/yaml/cyaml.py +177 -0
  27. outerbounds/_vendor/yaml/dumper.py +138 -0
  28. outerbounds/_vendor/yaml/emitter.py +1239 -0
  29. outerbounds/_vendor/yaml/error.py +94 -0
  30. outerbounds/_vendor/yaml/events.py +104 -0
  31. outerbounds/_vendor/yaml/loader.py +62 -0
  32. outerbounds/_vendor/yaml/nodes.py +51 -0
  33. outerbounds/_vendor/yaml/parser.py +629 -0
  34. outerbounds/_vendor/yaml/reader.py +208 -0
  35. outerbounds/_vendor/yaml/representer.py +378 -0
  36. outerbounds/_vendor/yaml/resolver.py +245 -0
  37. outerbounds/_vendor/yaml/scanner.py +1555 -0
  38. outerbounds/_vendor/yaml/serializer.py +127 -0
  39. outerbounds/_vendor/yaml/tokens.py +129 -0
  40. outerbounds/command_groups/cli.py +1 -1
  41. outerbounds/command_groups/local_setup_cli.py +1 -2
  42. outerbounds/command_groups/perimeters_cli.py +1 -2
  43. outerbounds/command_groups/workstations_cli.py +2 -2
  44. outerbounds/utils/kubeconfig.py +2 -2
  45. outerbounds/utils/metaflowconfig.py +1 -1
  46. outerbounds/vendor.py +159 -0
  47. {outerbounds-0.3.71.dist-info → outerbounds-0.3.75.dist-info}/METADATA +1 -3
  48. outerbounds-0.3.75.dist-info/RECORD +55 -0
  49. outerbounds-0.3.71.dist-info/RECORD +0 -15
  50. {outerbounds-0.3.71.dist-info → outerbounds-0.3.75.dist-info}/WHEEL +0 -0
  51. {outerbounds-0.3.71.dist-info → outerbounds-0.3.75.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,287 @@
1
+ import os
2
+ import typing as t
3
+ from gettext import gettext as _
4
+ from gettext import ngettext
5
+
6
+ from ._compat import get_text_stderr
7
+ from .utils import echo
8
+
9
+ if t.TYPE_CHECKING:
10
+ from .core import Context
11
+ from .core import Parameter
12
+
13
+
14
+ def _join_param_hints(
15
+ param_hint: t.Optional[t.Union[t.Sequence[str], str]]
16
+ ) -> t.Optional[str]:
17
+ if param_hint is not None and not isinstance(param_hint, str):
18
+ return " / ".join(repr(x) for x in param_hint)
19
+
20
+ return param_hint
21
+
22
+
23
+ class ClickException(Exception):
24
+ """An exception that Click can handle and show to the user."""
25
+
26
+ #: The exit code for this exception.
27
+ exit_code = 1
28
+
29
+ def __init__(self, message: str) -> None:
30
+ super().__init__(message)
31
+ self.message = message
32
+
33
+ def format_message(self) -> str:
34
+ return self.message
35
+
36
+ def __str__(self) -> str:
37
+ return self.message
38
+
39
+ def show(self, file: t.Optional[t.IO] = None) -> None:
40
+ if file is None:
41
+ file = get_text_stderr()
42
+
43
+ echo(_("Error: {message}").format(message=self.format_message()), file=file)
44
+
45
+
46
+ class UsageError(ClickException):
47
+ """An internal exception that signals a usage error. This typically
48
+ aborts any further handling.
49
+
50
+ :param message: the error message to display.
51
+ :param ctx: optionally the context that caused this error. Click will
52
+ fill in the context automatically in some situations.
53
+ """
54
+
55
+ exit_code = 2
56
+
57
+ def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None:
58
+ super().__init__(message)
59
+ self.ctx = ctx
60
+ self.cmd = self.ctx.command if self.ctx else None
61
+
62
+ def show(self, file: t.Optional[t.IO] = None) -> None:
63
+ if file is None:
64
+ file = get_text_stderr()
65
+ color = None
66
+ hint = ""
67
+ if (
68
+ self.ctx is not None
69
+ and self.ctx.command.get_help_option(self.ctx) is not None
70
+ ):
71
+ hint = _("Try '{command} {option}' for help.").format(
72
+ command=self.ctx.command_path, option=self.ctx.help_option_names[0]
73
+ )
74
+ hint = f"{hint}\n"
75
+ if self.ctx is not None:
76
+ color = self.ctx.color
77
+ echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color)
78
+ echo(
79
+ _("Error: {message}").format(message=self.format_message()),
80
+ file=file,
81
+ color=color,
82
+ )
83
+
84
+
85
+ class BadParameter(UsageError):
86
+ """An exception that formats out a standardized error message for a
87
+ bad parameter. This is useful when thrown from a callback or type as
88
+ Click will attach contextual information to it (for instance, which
89
+ parameter it is).
90
+
91
+ .. versionadded:: 2.0
92
+
93
+ :param param: the parameter object that caused this error. This can
94
+ be left out, and Click will attach this info itself
95
+ if possible.
96
+ :param param_hint: a string that shows up as parameter name. This
97
+ can be used as alternative to `param` in cases
98
+ where custom validation should happen. If it is
99
+ a string it's used as such, if it's a list then
100
+ each item is quoted and separated.
101
+ """
102
+
103
+ def __init__(
104
+ self,
105
+ message: str,
106
+ ctx: t.Optional["Context"] = None,
107
+ param: t.Optional["Parameter"] = None,
108
+ param_hint: t.Optional[str] = None,
109
+ ) -> None:
110
+ super().__init__(message, ctx)
111
+ self.param = param
112
+ self.param_hint = param_hint
113
+
114
+ def format_message(self) -> str:
115
+ if self.param_hint is not None:
116
+ param_hint = self.param_hint
117
+ elif self.param is not None:
118
+ param_hint = self.param.get_error_hint(self.ctx) # type: ignore
119
+ else:
120
+ return _("Invalid value: {message}").format(message=self.message)
121
+
122
+ return _("Invalid value for {param_hint}: {message}").format(
123
+ param_hint=_join_param_hints(param_hint), message=self.message
124
+ )
125
+
126
+
127
+ class MissingParameter(BadParameter):
128
+ """Raised if click required an option or argument but it was not
129
+ provided when invoking the script.
130
+
131
+ .. versionadded:: 4.0
132
+
133
+ :param param_type: a string that indicates the type of the parameter.
134
+ The default is to inherit the parameter type from
135
+ the given `param`. Valid values are ``'parameter'``,
136
+ ``'option'`` or ``'argument'``.
137
+ """
138
+
139
+ def __init__(
140
+ self,
141
+ message: t.Optional[str] = None,
142
+ ctx: t.Optional["Context"] = None,
143
+ param: t.Optional["Parameter"] = None,
144
+ param_hint: t.Optional[str] = None,
145
+ param_type: t.Optional[str] = None,
146
+ ) -> None:
147
+ super().__init__(message or "", ctx, param, param_hint)
148
+ self.param_type = param_type
149
+
150
+ def format_message(self) -> str:
151
+ if self.param_hint is not None:
152
+ param_hint: t.Optional[str] = self.param_hint
153
+ elif self.param is not None:
154
+ param_hint = self.param.get_error_hint(self.ctx) # type: ignore
155
+ else:
156
+ param_hint = None
157
+
158
+ param_hint = _join_param_hints(param_hint)
159
+ param_hint = f" {param_hint}" if param_hint else ""
160
+
161
+ param_type = self.param_type
162
+ if param_type is None and self.param is not None:
163
+ param_type = self.param.param_type_name
164
+
165
+ msg = self.message
166
+ if self.param is not None:
167
+ msg_extra = self.param.type.get_missing_message(self.param)
168
+ if msg_extra:
169
+ if msg:
170
+ msg += f". {msg_extra}"
171
+ else:
172
+ msg = msg_extra
173
+
174
+ msg = f" {msg}" if msg else ""
175
+
176
+ # Translate param_type for known types.
177
+ if param_type == "argument":
178
+ missing = _("Missing argument")
179
+ elif param_type == "option":
180
+ missing = _("Missing option")
181
+ elif param_type == "parameter":
182
+ missing = _("Missing parameter")
183
+ else:
184
+ missing = _("Missing {param_type}").format(param_type=param_type)
185
+
186
+ return f"{missing}{param_hint}.{msg}"
187
+
188
+ def __str__(self) -> str:
189
+ if not self.message:
190
+ param_name = self.param.name if self.param else None
191
+ return _("Missing parameter: {param_name}").format(param_name=param_name)
192
+ else:
193
+ return self.message
194
+
195
+
196
+ class NoSuchOption(UsageError):
197
+ """Raised if click attempted to handle an option that does not
198
+ exist.
199
+
200
+ .. versionadded:: 4.0
201
+ """
202
+
203
+ def __init__(
204
+ self,
205
+ option_name: str,
206
+ message: t.Optional[str] = None,
207
+ possibilities: t.Optional[t.Sequence[str]] = None,
208
+ ctx: t.Optional["Context"] = None,
209
+ ) -> None:
210
+ if message is None:
211
+ message = _("No such option: {name}").format(name=option_name)
212
+
213
+ super().__init__(message, ctx)
214
+ self.option_name = option_name
215
+ self.possibilities = possibilities
216
+
217
+ def format_message(self) -> str:
218
+ if not self.possibilities:
219
+ return self.message
220
+
221
+ possibility_str = ", ".join(sorted(self.possibilities))
222
+ suggest = ngettext(
223
+ "Did you mean {possibility}?",
224
+ "(Possible options: {possibilities})",
225
+ len(self.possibilities),
226
+ ).format(possibility=possibility_str, possibilities=possibility_str)
227
+ return f"{self.message} {suggest}"
228
+
229
+
230
+ class BadOptionUsage(UsageError):
231
+ """Raised if an option is generally supplied but the use of the option
232
+ was incorrect. This is for instance raised if the number of arguments
233
+ for an option is not correct.
234
+
235
+ .. versionadded:: 4.0
236
+
237
+ :param option_name: the name of the option being used incorrectly.
238
+ """
239
+
240
+ def __init__(
241
+ self, option_name: str, message: str, ctx: t.Optional["Context"] = None
242
+ ) -> None:
243
+ super().__init__(message, ctx)
244
+ self.option_name = option_name
245
+
246
+
247
+ class BadArgumentUsage(UsageError):
248
+ """Raised if an argument is generally supplied but the use of the argument
249
+ was incorrect. This is for instance raised if the number of values
250
+ for an argument is not correct.
251
+
252
+ .. versionadded:: 6.0
253
+ """
254
+
255
+
256
+ class FileError(ClickException):
257
+ """Raised if a file cannot be opened."""
258
+
259
+ def __init__(self, filename: str, hint: t.Optional[str] = None) -> None:
260
+ if hint is None:
261
+ hint = _("unknown error")
262
+
263
+ super().__init__(hint)
264
+ self.ui_filename = os.fsdecode(filename)
265
+ self.filename = filename
266
+
267
+ def format_message(self) -> str:
268
+ return _("Could not open file {filename!r}: {message}").format(
269
+ filename=self.ui_filename, message=self.message
270
+ )
271
+
272
+
273
+ class Abort(RuntimeError):
274
+ """An internal signalling exception that signals Click to abort."""
275
+
276
+
277
+ class Exit(RuntimeError):
278
+ """An exception that indicates that the application should exit with some
279
+ status code.
280
+
281
+ :param code: the status code to exit with.
282
+ """
283
+
284
+ __slots__ = ("exit_code",)
285
+
286
+ def __init__(self, code: int = 0) -> None:
287
+ self.exit_code = code
@@ -0,0 +1,301 @@
1
+ import typing as t
2
+ from contextlib import contextmanager
3
+ from gettext import gettext as _
4
+
5
+ from ._compat import term_len
6
+ from .parser import split_opt
7
+
8
+ # Can force a width. This is used by the test system
9
+ FORCED_WIDTH: t.Optional[int] = None
10
+
11
+
12
+ def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]:
13
+ widths: t.Dict[int, int] = {}
14
+
15
+ for row in rows:
16
+ for idx, col in enumerate(row):
17
+ widths[idx] = max(widths.get(idx, 0), term_len(col))
18
+
19
+ return tuple(y for x, y in sorted(widths.items()))
20
+
21
+
22
+ def iter_rows(
23
+ rows: t.Iterable[t.Tuple[str, str]], col_count: int
24
+ ) -> t.Iterator[t.Tuple[str, ...]]:
25
+ for row in rows:
26
+ yield row + ("",) * (col_count - len(row))
27
+
28
+
29
+ def wrap_text(
30
+ text: str,
31
+ width: int = 78,
32
+ initial_indent: str = "",
33
+ subsequent_indent: str = "",
34
+ preserve_paragraphs: bool = False,
35
+ ) -> str:
36
+ """A helper function that intelligently wraps text. By default, it
37
+ assumes that it operates on a single paragraph of text but if the
38
+ `preserve_paragraphs` parameter is provided it will intelligently
39
+ handle paragraphs (defined by two empty lines).
40
+
41
+ If paragraphs are handled, a paragraph can be prefixed with an empty
42
+ line containing the ``\\b`` character (``\\x08``) to indicate that
43
+ no rewrapping should happen in that block.
44
+
45
+ :param text: the text that should be rewrapped.
46
+ :param width: the maximum width for the text.
47
+ :param initial_indent: the initial indent that should be placed on the
48
+ first line as a string.
49
+ :param subsequent_indent: the indent string that should be placed on
50
+ each consecutive line.
51
+ :param preserve_paragraphs: if this flag is set then the wrapping will
52
+ intelligently handle paragraphs.
53
+ """
54
+ from ._textwrap import TextWrapper
55
+
56
+ text = text.expandtabs()
57
+ wrapper = TextWrapper(
58
+ width,
59
+ initial_indent=initial_indent,
60
+ subsequent_indent=subsequent_indent,
61
+ replace_whitespace=False,
62
+ )
63
+ if not preserve_paragraphs:
64
+ return wrapper.fill(text)
65
+
66
+ p: t.List[t.Tuple[int, bool, str]] = []
67
+ buf: t.List[str] = []
68
+ indent = None
69
+
70
+ def _flush_par() -> None:
71
+ if not buf:
72
+ return
73
+ if buf[0].strip() == "\b":
74
+ p.append((indent or 0, True, "\n".join(buf[1:])))
75
+ else:
76
+ p.append((indent or 0, False, " ".join(buf)))
77
+ del buf[:]
78
+
79
+ for line in text.splitlines():
80
+ if not line:
81
+ _flush_par()
82
+ indent = None
83
+ else:
84
+ if indent is None:
85
+ orig_len = term_len(line)
86
+ line = line.lstrip()
87
+ indent = orig_len - term_len(line)
88
+ buf.append(line)
89
+ _flush_par()
90
+
91
+ rv = []
92
+ for indent, raw, text in p:
93
+ with wrapper.extra_indent(" " * indent):
94
+ if raw:
95
+ rv.append(wrapper.indent_only(text))
96
+ else:
97
+ rv.append(wrapper.fill(text))
98
+
99
+ return "\n\n".join(rv)
100
+
101
+
102
+ class HelpFormatter:
103
+ """This class helps with formatting text-based help pages. It's
104
+ usually just needed for very special internal cases, but it's also
105
+ exposed so that developers can write their own fancy outputs.
106
+
107
+ At present, it always writes into memory.
108
+
109
+ :param indent_increment: the additional increment for each level.
110
+ :param width: the width for the text. This defaults to the terminal
111
+ width clamped to a maximum of 78.
112
+ """
113
+
114
+ def __init__(
115
+ self,
116
+ indent_increment: int = 2,
117
+ width: t.Optional[int] = None,
118
+ max_width: t.Optional[int] = None,
119
+ ) -> None:
120
+ import shutil
121
+
122
+ self.indent_increment = indent_increment
123
+ if max_width is None:
124
+ max_width = 80
125
+ if width is None:
126
+ width = FORCED_WIDTH
127
+ if width is None:
128
+ width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50)
129
+ self.width = width
130
+ self.current_indent = 0
131
+ self.buffer: t.List[str] = []
132
+
133
+ def write(self, string: str) -> None:
134
+ """Writes a unicode string into the internal buffer."""
135
+ self.buffer.append(string)
136
+
137
+ def indent(self) -> None:
138
+ """Increases the indentation."""
139
+ self.current_indent += self.indent_increment
140
+
141
+ def dedent(self) -> None:
142
+ """Decreases the indentation."""
143
+ self.current_indent -= self.indent_increment
144
+
145
+ def write_usage(
146
+ self, prog: str, args: str = "", prefix: t.Optional[str] = None
147
+ ) -> None:
148
+ """Writes a usage line into the buffer.
149
+
150
+ :param prog: the program name.
151
+ :param args: whitespace separated list of arguments.
152
+ :param prefix: The prefix for the first line. Defaults to
153
+ ``"Usage: "``.
154
+ """
155
+ if prefix is None:
156
+ prefix = f"{_('Usage:')} "
157
+
158
+ usage_prefix = f"{prefix:>{self.current_indent}}{prog} "
159
+ text_width = self.width - self.current_indent
160
+
161
+ if text_width >= (term_len(usage_prefix) + 20):
162
+ # The arguments will fit to the right of the prefix.
163
+ indent = " " * term_len(usage_prefix)
164
+ self.write(
165
+ wrap_text(
166
+ args,
167
+ text_width,
168
+ initial_indent=usage_prefix,
169
+ subsequent_indent=indent,
170
+ )
171
+ )
172
+ else:
173
+ # The prefix is too long, put the arguments on the next line.
174
+ self.write(usage_prefix)
175
+ self.write("\n")
176
+ indent = " " * (max(self.current_indent, term_len(prefix)) + 4)
177
+ self.write(
178
+ wrap_text(
179
+ args, text_width, initial_indent=indent, subsequent_indent=indent
180
+ )
181
+ )
182
+
183
+ self.write("\n")
184
+
185
+ def write_heading(self, heading: str) -> None:
186
+ """Writes a heading into the buffer."""
187
+ self.write(f"{'':>{self.current_indent}}{heading}:\n")
188
+
189
+ def write_paragraph(self) -> None:
190
+ """Writes a paragraph into the buffer."""
191
+ if self.buffer:
192
+ self.write("\n")
193
+
194
+ def write_text(self, text: str) -> None:
195
+ """Writes re-indented text into the buffer. This rewraps and
196
+ preserves paragraphs.
197
+ """
198
+ indent = " " * self.current_indent
199
+ self.write(
200
+ wrap_text(
201
+ text,
202
+ self.width,
203
+ initial_indent=indent,
204
+ subsequent_indent=indent,
205
+ preserve_paragraphs=True,
206
+ )
207
+ )
208
+ self.write("\n")
209
+
210
+ def write_dl(
211
+ self,
212
+ rows: t.Sequence[t.Tuple[str, str]],
213
+ col_max: int = 30,
214
+ col_spacing: int = 2,
215
+ ) -> None:
216
+ """Writes a definition list into the buffer. This is how options
217
+ and commands are usually formatted.
218
+
219
+ :param rows: a list of two item tuples for the terms and values.
220
+ :param col_max: the maximum width of the first column.
221
+ :param col_spacing: the number of spaces between the first and
222
+ second column.
223
+ """
224
+ rows = list(rows)
225
+ widths = measure_table(rows)
226
+ if len(widths) != 2:
227
+ raise TypeError("Expected two columns for definition list")
228
+
229
+ first_col = min(widths[0], col_max) + col_spacing
230
+
231
+ for first, second in iter_rows(rows, len(widths)):
232
+ self.write(f"{'':>{self.current_indent}}{first}")
233
+ if not second:
234
+ self.write("\n")
235
+ continue
236
+ if term_len(first) <= first_col - col_spacing:
237
+ self.write(" " * (first_col - term_len(first)))
238
+ else:
239
+ self.write("\n")
240
+ self.write(" " * (first_col + self.current_indent))
241
+
242
+ text_width = max(self.width - first_col - 2, 10)
243
+ wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True)
244
+ lines = wrapped_text.splitlines()
245
+
246
+ if lines:
247
+ self.write(f"{lines[0]}\n")
248
+
249
+ for line in lines[1:]:
250
+ self.write(f"{'':>{first_col + self.current_indent}}{line}\n")
251
+ else:
252
+ self.write("\n")
253
+
254
+ @contextmanager
255
+ def section(self, name: str) -> t.Iterator[None]:
256
+ """Helpful context manager that writes a paragraph, a heading,
257
+ and the indents.
258
+
259
+ :param name: the section name that is written as heading.
260
+ """
261
+ self.write_paragraph()
262
+ self.write_heading(name)
263
+ self.indent()
264
+ try:
265
+ yield
266
+ finally:
267
+ self.dedent()
268
+
269
+ @contextmanager
270
+ def indentation(self) -> t.Iterator[None]:
271
+ """A context manager that increases the indentation."""
272
+ self.indent()
273
+ try:
274
+ yield
275
+ finally:
276
+ self.dedent()
277
+
278
+ def getvalue(self) -> str:
279
+ """Returns the buffer contents."""
280
+ return "".join(self.buffer)
281
+
282
+
283
+ def join_options(options: t.Sequence[str]) -> t.Tuple[str, bool]:
284
+ """Given a list of option strings this joins them in the most appropriate
285
+ way and returns them in the form ``(formatted_string,
286
+ any_prefix_is_slash)`` where the second item in the tuple is a flag that
287
+ indicates if any of the option prefixes was a slash.
288
+ """
289
+ rv = []
290
+ any_prefix_is_slash = False
291
+
292
+ for opt in options:
293
+ prefix = split_opt(opt)[0]
294
+
295
+ if prefix == "/":
296
+ any_prefix_is_slash = True
297
+
298
+ rv.append((len(prefix), opt))
299
+
300
+ rv.sort(key=lambda x: x[0])
301
+ return ", ".join(x[1] for x in rv), any_prefix_is_slash
@@ -0,0 +1,68 @@
1
+ import typing as t
2
+ from threading import local
3
+
4
+ if t.TYPE_CHECKING:
5
+ import typing_extensions as te
6
+ from .core import Context
7
+
8
+ _local = local()
9
+
10
+
11
+ @t.overload
12
+ def get_current_context(silent: "te.Literal[False]" = False) -> "Context":
13
+ ...
14
+
15
+
16
+ @t.overload
17
+ def get_current_context(silent: bool = ...) -> t.Optional["Context"]:
18
+ ...
19
+
20
+
21
+ def get_current_context(silent: bool = False) -> t.Optional["Context"]:
22
+ """Returns the current click context. This can be used as a way to
23
+ access the current context object from anywhere. This is a more implicit
24
+ alternative to the :func:`pass_context` decorator. This function is
25
+ primarily useful for helpers such as :func:`echo` which might be
26
+ interested in changing its behavior based on the current context.
27
+
28
+ To push the current context, :meth:`Context.scope` can be used.
29
+
30
+ .. versionadded:: 5.0
31
+
32
+ :param silent: if set to `True` the return value is `None` if no context
33
+ is available. The default behavior is to raise a
34
+ :exc:`RuntimeError`.
35
+ """
36
+ try:
37
+ return t.cast("Context", _local.stack[-1])
38
+ except (AttributeError, IndexError) as e:
39
+ if not silent:
40
+ raise RuntimeError("There is no active click context.") from e
41
+
42
+ return None
43
+
44
+
45
+ def push_context(ctx: "Context") -> None:
46
+ """Pushes a new context to the current stack."""
47
+ _local.__dict__.setdefault("stack", []).append(ctx)
48
+
49
+
50
+ def pop_context() -> None:
51
+ """Removes the top level from the stack."""
52
+ _local.stack.pop()
53
+
54
+
55
+ def resolve_color_default(color: t.Optional[bool] = None) -> t.Optional[bool]:
56
+ """Internal helper to get the default value of the color flag. If a
57
+ value is passed it's returned unchanged, otherwise it's looked up from
58
+ the current context.
59
+ """
60
+ if color is not None:
61
+ return color
62
+
63
+ ctx = get_current_context(silent=True)
64
+
65
+ if ctx is not None:
66
+ return ctx.color
67
+
68
+ return None