Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/traitlets/utils/descriptions.py: 37%
68 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
1from __future__ import annotations
3import inspect
4import re
5import types
6from typing import Any
9def describe(
10 article: str | None,
11 value: Any,
12 name: str | None = None,
13 verbose: bool = False,
14 capital: bool = False,
15) -> str:
16 """Return string that describes a value
18 Parameters
19 ----------
20 article : str or None
21 A definite or indefinite article. If the article is
22 indefinite (i.e. "a" or "an") the appropriate one
23 will be inferred. Thus, the arguments of ``describe``
24 can themselves represent what the resulting string
25 will actually look like. If None, then no article
26 will be prepended to the result. For non-articled
27 description, values that are instances are treated
28 definitely, while classes are handled indefinitely.
29 value : any
30 The value which will be named.
31 name : str or None (default: None)
32 Only applies when ``article`` is "the" - this
33 ``name`` is a definite reference to the value.
34 By default one will be inferred from the value's
35 type and repr methods.
36 verbose : bool (default: False)
37 Whether the name should be concise or verbose. When
38 possible, verbose names include the module, and/or
39 class name where an object was defined.
40 capital : bool (default: False)
41 Whether the first letter of the article should
42 be capitalized or not. By default it is not.
44 Examples
45 --------
46 Indefinite description:
48 >>> describe("a", object())
49 'an object'
50 >>> describe("a", object)
51 'an object'
52 >>> describe("a", type(object))
53 'a type'
55 Definite description:
57 >>> describe("the", object())
58 "the object at '...'"
59 >>> describe("the", object)
60 'the object object'
61 >>> describe("the", type(object))
62 'the type type'
64 Definitely named description:
66 >>> describe("the", object(), "I made")
67 'the object I made'
68 >>> describe("the", object, "I will use")
69 'the object I will use'
70 """
71 if isinstance(article, str):
72 article = article.lower()
74 if not inspect.isclass(value):
75 typename = type(value).__name__
76 else:
77 typename = value.__name__
78 if verbose:
79 typename = _prefix(value) + typename
81 if article == "the" or (article is None and not inspect.isclass(value)):
82 if name is not None:
83 result = f"{typename} {name}"
84 if article is not None:
85 return add_article(result, True, capital)
86 else:
87 return result
88 else:
89 tick_wrap = False
90 if inspect.isclass(value):
91 name = value.__name__
92 elif isinstance(value, types.FunctionType):
93 name = value.__name__
94 tick_wrap = True
95 elif isinstance(value, types.MethodType):
96 name = value.__func__.__name__
97 tick_wrap = True
98 elif type(value).__repr__ in (
99 object.__repr__,
100 type.__repr__,
101 ): # type:ignore[comparison-overlap]
102 name = "at '%s'" % hex(id(value))
103 verbose = False
104 else:
105 name = repr(value)
106 verbose = False
107 if verbose:
108 name = _prefix(value) + name
109 if tick_wrap:
110 name = name.join("''")
111 return describe(article, value, name=name, verbose=verbose, capital=capital)
112 elif article in ("a", "an") or article is None:
113 if article is None:
114 return typename
115 return add_article(typename, False, capital)
116 else:
117 raise ValueError(
118 "The 'article' argument should be 'the', 'a', 'an', or None not %r" % article
119 )
122def _prefix(value: Any) -> str:
123 if isinstance(value, types.MethodType):
124 name = describe(None, value.__self__, verbose=True) + "."
125 else:
126 module = inspect.getmodule(value)
127 if module is not None and module.__name__ != "builtins":
128 name = module.__name__ + "."
129 else:
130 name = ""
131 return name
134def class_of(value: Any) -> Any:
135 """Returns a string of the value's type with an indefinite article.
137 For example 'an Image' or 'a PlotValue'.
138 """
139 if inspect.isclass(value):
140 return add_article(value.__name__)
141 else:
142 return class_of(type(value))
145def add_article(name: str, definite: bool = False, capital: bool = False) -> str:
146 """Returns the string with a prepended article.
148 The input does not need to begin with a character.
150 Parameters
151 ----------
152 name : str
153 Name to which to prepend an article
154 definite : bool (default: False)
155 Whether the article is definite or not.
156 Indefinite articles being 'a' and 'an',
157 while 'the' is definite.
158 capital : bool (default: False)
159 Whether the added article should have
160 its first letter capitalized or not.
161 """
162 if definite:
163 result = "the " + name
164 else:
165 first_letters = re.compile(r"[\W_]+").sub("", name)
166 if first_letters[:1].lower() in "aeiou":
167 result = "an " + name
168 else:
169 result = "a " + name
170 if capital:
171 return result[0].upper() + result[1:]
172 else:
173 return result
176def repr_type(obj: Any) -> str:
177 """Return a string representation of a value and its type for readable
179 error messages.
180 """
181 the_type = type(obj)
182 return f"{obj!r} {the_type!r}"