extended-data 8.0.0__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 (63) hide show
  1. extended_data/__init__.py +105 -0
  2. extended_data/_version.py +16 -0
  3. extended_data/cli.py +204 -0
  4. extended_data/containers/__init__.py +17 -0
  5. extended_data/containers/factory.py +57 -0
  6. extended_data/containers/mappings.py +181 -0
  7. extended_data/containers/sequences.py +403 -0
  8. extended_data/containers/strings.py +276 -0
  9. extended_data/inputs/__init__.py +14 -0
  10. extended_data/inputs/__main__.py +412 -0
  11. extended_data/inputs/decorators.py +346 -0
  12. extended_data/inputs/py.typed +0 -0
  13. extended_data/io/__init__.py +50 -0
  14. extended_data/io/base64.py +65 -0
  15. extended_data/io/exporters.py +151 -0
  16. extended_data/io/files.py +619 -0
  17. extended_data/io/importers.py +53 -0
  18. extended_data/logging/__init__.py +13 -0
  19. extended_data/logging/const.py +15 -0
  20. extended_data/logging/handlers.py +61 -0
  21. extended_data/logging/log_types.py +18 -0
  22. extended_data/logging/logging.py +663 -0
  23. extended_data/logging/py.typed +0 -0
  24. extended_data/logging/utils.py +167 -0
  25. extended_data/primitives/__init__.py +172 -0
  26. extended_data/primitives/formats/__init__.py +47 -0
  27. extended_data/primitives/formats/_normalization.py +15 -0
  28. extended_data/primitives/formats/errors.py +72 -0
  29. extended_data/primitives/formats/hcl.py +264 -0
  30. extended_data/primitives/formats/json.py +100 -0
  31. extended_data/primitives/formats/toml.py +48 -0
  32. extended_data/primitives/formats/yaml/__init__.py +43 -0
  33. extended_data/primitives/formats/yaml/constructors.py +58 -0
  34. extended_data/primitives/formats/yaml/dumpers.py +75 -0
  35. extended_data/primitives/formats/yaml/loaders.py +31 -0
  36. extended_data/primitives/formats/yaml/representers.py +82 -0
  37. extended_data/primitives/formats/yaml/tag_classes.py +79 -0
  38. extended_data/primitives/formats/yaml/utils.py +75 -0
  39. extended_data/primitives/introspection.py +168 -0
  40. extended_data/primitives/mappings.py +399 -0
  41. extended_data/primitives/matching.py +82 -0
  42. extended_data/primitives/numbers.py +236 -0
  43. extended_data/primitives/redaction.py +126 -0
  44. extended_data/primitives/sequences.py +85 -0
  45. extended_data/primitives/serialization.py +37 -0
  46. extended_data/primitives/splitting.py +56 -0
  47. extended_data/primitives/state.py +162 -0
  48. extended_data/primitives/string_transforms.py +74 -0
  49. extended_data/primitives/strings.py +127 -0
  50. extended_data/primitives/transformations/__init__.py +13 -0
  51. extended_data/primitives/transformations/numbers/__init__.py +40 -0
  52. extended_data/primitives/transformations/numbers/notation.py +181 -0
  53. extended_data/primitives/transformations/numbers/words.py +403 -0
  54. extended_data/primitives/transformations/strings/__init__.py +26 -0
  55. extended_data/primitives/transformations/strings/inflection.py +95 -0
  56. extended_data/primitives/types.py +532 -0
  57. extended_data/py.typed +0 -0
  58. extended_data/workflows/__init__.py +394 -0
  59. extended_data-8.0.0.dist-info/METADATA +189 -0
  60. extended_data-8.0.0.dist-info/RECORD +63 -0
  61. extended_data-8.0.0.dist-info/WHEEL +4 -0
  62. extended_data-8.0.0.dist-info/entry_points.txt +2 -0
  63. extended_data-8.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,403 @@
1
+ """Extended sequence containers built on Tier 1 primitives."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections import UserList
6
+ from collections.abc import Callable, Iterable, Iterator, MutableSet
7
+ from operator import index as operator_index
8
+ from typing import Any, SupportsIndex, TypeVar, cast, overload
9
+
10
+ from extended_data.containers.mappings import ExtendedDict
11
+ from extended_data.primitives.mappings import zipmap as primitive_zipmap
12
+ from extended_data.primitives.sequences import filter_list, flatten_list
13
+ from extended_data.primitives.splitting import split_list_by_type
14
+ from extended_data.primitives.state import first_non_empty as primitive_first_non_empty
15
+ from extended_data.primitives.state import is_nothing
16
+ from extended_data.primitives.types import make_hashable, reconstruct_special_types
17
+
18
+
19
+ T = TypeVar("T")
20
+ U = TypeVar("U")
21
+
22
+
23
+ class ExtendedList(UserList[T]):
24
+ """List wrapper with chainable primitive operations."""
25
+
26
+ def __init__(self, initlist: Iterable[T] | None = None) -> None:
27
+ """Initialize the extended list."""
28
+ super().__init__()
29
+ self.extend(initlist or [])
30
+
31
+ @staticmethod
32
+ def _wrap_item(item: T) -> T:
33
+ """Promote nested built-in containers to extended containers."""
34
+ from extended_data.containers.factory import extend_data
35
+
36
+ return cast(T, extend_data(item))
37
+
38
+ @overload
39
+ def __setitem__(self, i: SupportsIndex, item: T) -> None: ...
40
+
41
+ @overload
42
+ def __setitem__(self, i: slice, item: Iterable[T]) -> None: ...
43
+
44
+ def __setitem__(self, i: SupportsIndex | slice, item: T | Iterable[T]) -> None:
45
+ """Set values while preserving extended nested containers."""
46
+ if isinstance(i, slice):
47
+ self.data[i] = [self._wrap_item(value) for value in cast(Iterable[T], item)]
48
+ return
49
+ self.data[i] = self._wrap_item(cast(T, item))
50
+
51
+ def append(self, item: T) -> None:
52
+ """Append a value while preserving extended nested containers."""
53
+ self.data.append(self._wrap_item(item))
54
+
55
+ def extend(self, other: Iterable[T]) -> None:
56
+ """Extend values while preserving extended nested containers."""
57
+ self.data.extend(self._wrap_item(item) for item in other)
58
+
59
+ def __iadd__(self, other: Iterable[T]) -> ExtendedList[T]:
60
+ """Extend in place while preserving extended nested containers."""
61
+ self.extend(other)
62
+ return self
63
+
64
+ def __imul__(self, count: SupportsIndex) -> ExtendedList[T]:
65
+ """Repeat in place while preserving extended nested containers."""
66
+ self.data *= operator_index(count)
67
+ self.data[:] = [self._wrap_item(item) for item in self.data]
68
+ return self
69
+
70
+ def insert(self, i: int, item: T) -> None:
71
+ """Insert a value while preserving extended nested containers."""
72
+ self.data.insert(i, self._wrap_item(item))
73
+
74
+ def flatten(self) -> ExtendedList[Any]:
75
+ """Return a recursively flattened copy."""
76
+ from extended_data.containers.factory import extend_data, to_builtin
77
+
78
+ return extend_data(flatten_list(to_builtin(self.data)))
79
+
80
+ def compact(self) -> ExtendedList[T]:
81
+ """Return a copy without values considered empty."""
82
+ return ExtendedList(item for item in self.data if not is_nothing(item))
83
+
84
+ def map(self, func: Callable[[T], U]) -> ExtendedList[U]:
85
+ """Return a copy with a callable applied to each item."""
86
+ return ExtendedList(func(item) for item in self.data)
87
+
88
+ def filter(self, predicate: Callable[[T], bool]) -> ExtendedList[T]:
89
+ """Return a copy containing items accepted by a predicate."""
90
+ return ExtendedList(item for item in self.data if predicate(item))
91
+
92
+ def filter_values(
93
+ self,
94
+ *,
95
+ allowlist: Iterable[T] | None = None,
96
+ denylist: Iterable[T] | None = None,
97
+ ) -> ExtendedList[T]:
98
+ """Return a copy filtered by explicit allowed and denied values."""
99
+ return ExtendedList(filter_list(self.data, allowlist=allowlist, denylist=denylist))
100
+
101
+ def split_by_type(self, *, primitive_only: bool = False) -> ExtendedDict:
102
+ """Return values grouped by type name."""
103
+ from extended_data.containers.factory import extend_data, to_builtin
104
+
105
+ grouped = split_list_by_type(to_builtin(self.data), primitive_only=primitive_only)
106
+ return extend_data({type_key.__name__: values for type_key, values in grouped.items()})
107
+
108
+ def first_non_empty(self) -> T | None:
109
+ """Return the first value not considered empty."""
110
+ return cast(T | None, primitive_first_non_empty(*self.data))
111
+
112
+ def zipmap(self, values: Iterable[str]) -> ExtendedDict:
113
+ """Return an extended mapping from this list's values to provided values."""
114
+ from extended_data.containers.factory import extend_data, to_builtin
115
+
116
+ keys = [str(item) for item in to_builtin(self.data)]
117
+ mapped_values = [str(item) for item in to_builtin(list(values))]
118
+ return extend_data(primitive_zipmap(keys, mapped_values))
119
+
120
+ def reconstruct_special_types(self, *, fail_silently: bool = False) -> ExtendedList[Any]:
121
+ """Return a copy with string-like special values reconstructed."""
122
+ from extended_data.containers.factory import extend_data, to_builtin
123
+
124
+ return extend_data(reconstruct_special_types(to_builtin(self.data), fail_silently=fail_silently))
125
+
126
+ def to_export_safe(self, *, export_to_yaml: bool = False) -> Any:
127
+ """Return this list converted to export-safe primitive data."""
128
+ from extended_data.io.exporters import make_raw_data_export_safe
129
+
130
+ return make_raw_data_export_safe(self.data, export_to_yaml=export_to_yaml)
131
+
132
+ def wrap_for_export(self, allow_encoding: bool | str = True, **format_opts: Any) -> str:
133
+ """Return this list wrapped as an encoded export string."""
134
+ from extended_data.io.exporters import wrap_raw_data_for_export
135
+
136
+ return wrap_raw_data_for_export(self.data, allow_encoding=allow_encoding, **format_opts)
137
+
138
+ def unique(self) -> ExtendedList[T]:
139
+ """Return a copy with duplicate values removed while preserving order."""
140
+ seen: set[Any] = set()
141
+ values: list[T] = []
142
+ for item in self.data:
143
+ marker = make_hashable(item)
144
+ if marker in seen:
145
+ continue
146
+ seen.add(marker)
147
+ values.append(item)
148
+ return ExtendedList(values)
149
+
150
+
151
+ class ExtendedTuple(tuple[T, ...]):
152
+ """Tuple wrapper with immutable chainable sequence operations."""
153
+
154
+ __slots__ = ()
155
+
156
+ def __new__(cls, values: Iterable[T] | None = None) -> ExtendedTuple[T]:
157
+ """Initialize the extended tuple."""
158
+ items = () if values is None else values
159
+ return super().__new__(cls, (cls._wrap_item(item) for item in items))
160
+
161
+ @staticmethod
162
+ def _wrap_item(item: T) -> T:
163
+ """Promote nested built-in containers to extended containers."""
164
+ from extended_data.containers.factory import extend_data
165
+
166
+ return cast(T, extend_data(item))
167
+
168
+ @overload
169
+ def __getitem__(self, index: SupportsIndex) -> T: ...
170
+
171
+ @overload
172
+ def __getitem__(self, index: slice) -> ExtendedTuple[T]: ...
173
+
174
+ def __getitem__(self, index: SupportsIndex | slice) -> T | ExtendedTuple[T]:
175
+ """Return sliced values as ExtendedTuple instances."""
176
+ value = super().__getitem__(index)
177
+ if isinstance(index, slice):
178
+ return ExtendedTuple(cast(tuple[T, ...], value))
179
+ return cast(T, value)
180
+
181
+ @overload
182
+ def __add__(self, other: tuple[T, ...]) -> ExtendedTuple[T]: ...
183
+
184
+ @overload
185
+ def __add__(self, other: tuple[U, ...]) -> ExtendedTuple[T | U]: ...
186
+
187
+ def __add__(self, other: tuple[Any, ...]) -> ExtendedTuple[Any]:
188
+ """Concatenate tuples while preserving the ExtendedTuple surface."""
189
+ return ExtendedTuple((*tuple(self), *other))
190
+
191
+ def __radd__(self, other: tuple[Any, ...]) -> ExtendedTuple[Any]:
192
+ """Concatenate tuples while preserving the ExtendedTuple surface."""
193
+ return ExtendedTuple((*other, *tuple(self)))
194
+
195
+ def __mul__(self, count: SupportsIndex) -> ExtendedTuple[T]:
196
+ """Repeat tuple values while preserving the ExtendedTuple surface."""
197
+ return ExtendedTuple(tuple(self) * operator_index(count))
198
+
199
+ def __rmul__(self, count: SupportsIndex) -> ExtendedTuple[T]:
200
+ """Repeat tuple values while preserving the ExtendedTuple surface."""
201
+ return self * count
202
+
203
+ def flatten(self) -> ExtendedTuple[Any]:
204
+ """Return a recursively flattened tuple copy."""
205
+ from extended_data.containers.factory import to_builtin
206
+
207
+ def _flatten(items: Iterable[Any]) -> list[Any]:
208
+ flattened: list[Any] = []
209
+ for item in items:
210
+ plain_item = to_builtin(item)
211
+ if isinstance(plain_item, list | tuple):
212
+ flattened.extend(_flatten(plain_item))
213
+ else:
214
+ flattened.append(plain_item)
215
+ return flattened
216
+
217
+ return ExtendedTuple(_flatten(self))
218
+
219
+ def compact(self) -> ExtendedTuple[T]:
220
+ """Return a copy without values considered empty."""
221
+ return ExtendedTuple(item for item in self if not is_nothing(item))
222
+
223
+ def map(self, func: Callable[[T], U]) -> ExtendedTuple[U]:
224
+ """Return a copy with a callable applied to each item."""
225
+ return ExtendedTuple(func(item) for item in self)
226
+
227
+ def filter(self, predicate: Callable[[T], bool]) -> ExtendedTuple[T]:
228
+ """Return a copy containing items accepted by a predicate."""
229
+ return ExtendedTuple(item for item in self if predicate(item))
230
+
231
+ def unique(self) -> ExtendedTuple[T]:
232
+ """Return a copy with duplicate values removed while preserving order."""
233
+ seen: set[Any] = set()
234
+ values: list[T] = []
235
+ for item in self:
236
+ marker = make_hashable(item)
237
+ if marker in seen:
238
+ continue
239
+ seen.add(marker)
240
+ values.append(item)
241
+ return ExtendedTuple(values)
242
+
243
+ def split_by_type(self, *, primitive_only: bool = False) -> ExtendedDict:
244
+ """Return values grouped by type name while keeping tuple-shaped groups."""
245
+ from extended_data.containers.factory import extend_data, to_builtin
246
+
247
+ grouped = split_list_by_type(list(to_builtin(self)), primitive_only=primitive_only)
248
+ return extend_data({type_key.__name__: tuple(values) for type_key, values in grouped.items()})
249
+
250
+ def first_non_empty(self) -> T | None:
251
+ """Return the first value not considered empty."""
252
+ return cast(T | None, primitive_first_non_empty(*self))
253
+
254
+ def zipmap(self, values: Iterable[str]) -> ExtendedDict:
255
+ """Return an extended mapping from this tuple's values to provided values."""
256
+ from extended_data.containers.factory import extend_data, to_builtin
257
+
258
+ keys = [str(item) for item in to_builtin(tuple(self))]
259
+ mapped_values = [str(item) for item in to_builtin(list(values))]
260
+ return extend_data(primitive_zipmap(keys, mapped_values))
261
+
262
+ def reconstruct_special_types(self, *, fail_silently: bool = False) -> ExtendedTuple[Any]:
263
+ """Return a copy with string-like special values reconstructed."""
264
+ from extended_data.containers.factory import extend_data, to_builtin
265
+
266
+ return extend_data(reconstruct_special_types(to_builtin(tuple(self)), fail_silently=fail_silently))
267
+
268
+ def to_export_safe(self, *, export_to_yaml: bool = False) -> Any:
269
+ """Return this tuple converted to export-safe primitive data."""
270
+ from extended_data.io.exporters import make_raw_data_export_safe
271
+
272
+ return make_raw_data_export_safe(tuple(self), export_to_yaml=export_to_yaml)
273
+
274
+ def wrap_for_export(self, allow_encoding: bool | str = True, **format_opts: Any) -> str:
275
+ """Return this tuple wrapped as an encoded export string."""
276
+ from extended_data.io.exporters import wrap_raw_data_for_export
277
+
278
+ return wrap_raw_data_for_export(tuple(self), allow_encoding=allow_encoding, **format_opts)
279
+
280
+ def to_tuple(self) -> tuple[T, ...]:
281
+ """Return a plain tuple copy."""
282
+ return tuple(self)
283
+
284
+
285
+ class ExtendedSet(MutableSet[T]):
286
+ """Set wrapper with explicit chainable operations."""
287
+
288
+ def __init__(self, values: Iterable[T] | None = None) -> None:
289
+ """Initialize the extended set."""
290
+ self._data: set[T] = set()
291
+ for value in values or []:
292
+ self.add(value)
293
+
294
+ @staticmethod
295
+ def _wrap_item(item: T) -> T:
296
+ """Promote nested built-in containers to extended containers."""
297
+ from extended_data.containers.factory import extend_data
298
+
299
+ return cast(T, extend_data(item))
300
+
301
+ def __contains__(self, value: object) -> bool:
302
+ """Return whether the set contains a value."""
303
+ return value in self._data
304
+
305
+ def __iter__(self) -> Iterator[T]:
306
+ """Iterate over set values."""
307
+ return iter(self._data)
308
+
309
+ def __len__(self) -> int:
310
+ """Return the number of set values."""
311
+ return len(self._data)
312
+
313
+ def __repr__(self) -> str:
314
+ """Return a value-oriented representation."""
315
+ return f"{self.__class__.__name__}({self._data!r})"
316
+
317
+ def add(self, value: T) -> None:
318
+ """Add a value to the set."""
319
+ self._data.add(self._wrap_item(value))
320
+
321
+ def update(self, *others: Iterable[T]) -> None:
322
+ """Add values from one or more iterables."""
323
+ for other in others:
324
+ for value in other:
325
+ self.add(value)
326
+
327
+ def discard(self, value: T) -> None:
328
+ """Remove a value from the set if present."""
329
+ self._data.discard(value)
330
+
331
+ def copy(self) -> ExtendedSet[T]:
332
+ """Return a shallow copy."""
333
+ return ExtendedSet(self._data)
334
+
335
+ def compact(self) -> ExtendedSet[T]:
336
+ """Return a copy without values considered empty."""
337
+ return ExtendedSet(item for item in self._data if not is_nothing(item))
338
+
339
+ def reconstruct_special_types(self, *, fail_silently: bool = False) -> ExtendedSet[Any]:
340
+ """Return a copy with string-like special values reconstructed."""
341
+ from extended_data.containers.factory import extend_data, to_builtin
342
+
343
+ return extend_data(reconstruct_special_types(to_builtin(self._data), fail_silently=fail_silently))
344
+
345
+ def to_export_safe(self, *, export_to_yaml: bool = False) -> Any:
346
+ """Return this set converted to export-safe primitive data."""
347
+ from extended_data.io.exporters import make_raw_data_export_safe
348
+
349
+ return make_raw_data_export_safe(self._data, export_to_yaml=export_to_yaml)
350
+
351
+ def wrap_for_export(self, allow_encoding: bool | str = True, **format_opts: Any) -> str:
352
+ """Return this set wrapped as an encoded export string."""
353
+ from extended_data.io.exporters import wrap_raw_data_for_export
354
+
355
+ return wrap_raw_data_for_export(self._data, allow_encoding=allow_encoding, **format_opts)
356
+
357
+ def union(self, *others: Iterable[T]) -> ExtendedSet[T]:
358
+ """Return a union with other iterables."""
359
+ result = set(self._data)
360
+ for other in others:
361
+ result.update(other)
362
+ return ExtendedSet(result)
363
+
364
+ def intersection(self, *others: Iterable[T]) -> ExtendedSet[T]:
365
+ """Return an intersection with other iterables."""
366
+ result = set(self._data)
367
+ for other in others:
368
+ result.intersection_update(other)
369
+ return ExtendedSet(result)
370
+
371
+ def difference(self, *others: Iterable[T]) -> ExtendedSet[T]:
372
+ """Return a difference against other iterables."""
373
+ result = set(self._data)
374
+ for other in others:
375
+ result.difference_update(other)
376
+ return ExtendedSet(result)
377
+
378
+ def symmetric_difference(self, other: Iterable[T]) -> ExtendedSet[T]:
379
+ """Return a symmetric difference against another iterable."""
380
+ result = set(self._data)
381
+ for value in other:
382
+ wrapped = self._wrap_item(value)
383
+ if wrapped in result:
384
+ result.remove(wrapped)
385
+ else:
386
+ result.add(wrapped)
387
+ return ExtendedSet(result)
388
+
389
+ def intersection_update(self, *others: Iterable[T]) -> None:
390
+ """Keep only values found in all other iterables."""
391
+ self._data = self.intersection(*others)._data
392
+
393
+ def difference_update(self, *others: Iterable[T]) -> None:
394
+ """Remove values found in other iterables."""
395
+ self._data = self.difference(*others)._data
396
+
397
+ def symmetric_difference_update(self, other: Iterable[T]) -> None:
398
+ """Replace values with the symmetric difference against another iterable."""
399
+ self._data = self.symmetric_difference(other)._data
400
+
401
+ def to_set(self) -> set[T]:
402
+ """Return a plain set copy."""
403
+ return set(self._data)
@@ -0,0 +1,276 @@
1
+ """Extended string container built on Tier 1 primitives."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import datetime
6
+
7
+ from collections import UserString
8
+ from collections.abc import Iterable, Mapping
9
+ from pathlib import Path
10
+ from typing import TYPE_CHECKING, Any
11
+
12
+ import extended_data.primitives.matching as primitive_matching
13
+
14
+ from extended_data.primitives.string_transforms import (
15
+ humanize,
16
+ ordinalize,
17
+ pluralize,
18
+ singularize,
19
+ titleize,
20
+ to_camel_case,
21
+ to_kebab_case,
22
+ to_pascal_case,
23
+ to_snake_case,
24
+ )
25
+ from extended_data.primitives.strings import (
26
+ is_url,
27
+ lower_first_char,
28
+ sanitize_key,
29
+ titleize_name,
30
+ truncate,
31
+ upper_first_char,
32
+ )
33
+ from extended_data.primitives.types import (
34
+ reconstruct_special_type,
35
+ string_to_bool,
36
+ string_to_date,
37
+ string_to_datetime,
38
+ string_to_float,
39
+ string_to_int,
40
+ string_to_path,
41
+ string_to_time,
42
+ )
43
+
44
+
45
+ if TYPE_CHECKING:
46
+ from extended_data.containers.sequences import ExtendedList, ExtendedTuple
47
+
48
+
49
+ def _coerce_string_argument(value: str | UserString) -> str:
50
+ """Coerce stdlib user strings while preserving normal str errors elsewhere."""
51
+ return str(value) if isinstance(value, UserString) else value
52
+
53
+
54
+ class ExtendedString(UserString):
55
+ """String wrapper with chainable primitive operations."""
56
+
57
+ def lower_first(self) -> ExtendedString:
58
+ """Return a copy with the first character lowercased."""
59
+ return ExtendedString(lower_first_char(self.data))
60
+
61
+ def upper_first(self) -> ExtendedString:
62
+ """Return a copy with the first character uppercased."""
63
+ return ExtendedString(upper_first_char(self.data))
64
+
65
+ def remove_prefix(self, prefix: str) -> ExtendedString:
66
+ """Return a copy with a leading prefix removed."""
67
+ return ExtendedString(self.data.removeprefix(str(prefix)))
68
+
69
+ def remove_suffix(self, suffix: str) -> ExtendedString:
70
+ """Return a copy with a trailing suffix removed."""
71
+ return ExtendedString(self.data.removesuffix(str(suffix)))
72
+
73
+ def sanitize(self, delim: str = "_") -> ExtendedString:
74
+ """Return a key-safe copy."""
75
+ return ExtendedString(sanitize_key(self.data, delim=delim))
76
+
77
+ def truncate(self, max_length: int, ender: str = "...") -> ExtendedString:
78
+ """Return a truncated copy."""
79
+ return ExtendedString(truncate(self.data, max_length=max_length, ender=ender))
80
+
81
+ def titleize_name(self) -> ExtendedString:
82
+ """Return a titleized name copy."""
83
+ return ExtendedString(titleize_name(self.data))
84
+
85
+ def to_snake_case(self) -> ExtendedString:
86
+ """Return a snake_case copy."""
87
+ return ExtendedString(to_snake_case(self.data))
88
+
89
+ def to_camel_case(self, *, uppercase_first: bool = False) -> ExtendedString:
90
+ """Return a camelCase copy."""
91
+ return ExtendedString(to_camel_case(self.data, uppercase_first=uppercase_first))
92
+
93
+ def to_pascal_case(self) -> ExtendedString:
94
+ """Return a PascalCase copy."""
95
+ return ExtendedString(to_pascal_case(self.data))
96
+
97
+ def to_kebab_case(self) -> ExtendedString:
98
+ """Return a kebab-case copy."""
99
+ return ExtendedString(to_kebab_case(self.data))
100
+
101
+ def pluralize(self) -> ExtendedString:
102
+ """Return a pluralized copy."""
103
+ return ExtendedString(pluralize(self.data))
104
+
105
+ def singularize(self) -> ExtendedString:
106
+ """Return a singularized copy."""
107
+ return ExtendedString(singularize(self.data))
108
+
109
+ def humanize(self) -> ExtendedString:
110
+ """Return a human-readable copy."""
111
+ return ExtendedString(humanize(self.data))
112
+
113
+ def titleize(self) -> ExtendedString:
114
+ """Return a title-case copy."""
115
+ return ExtendedString(titleize(self.data))
116
+
117
+ def ordinalize(self) -> ExtendedString:
118
+ """Return an ordinalized copy."""
119
+ return ExtendedString(ordinalize(self.data))
120
+
121
+ def format(self, *args: object, **kwargs: object) -> ExtendedString: # type: ignore[override]
122
+ """Format values into an extended string."""
123
+ return ExtendedString(self.data.format(*args, **kwargs))
124
+
125
+ def format_map(self, mapping: Mapping[str, object]) -> ExtendedString: # type: ignore[override]
126
+ """Format mapping values into an extended string."""
127
+ return ExtendedString(self.data.format_map(mapping))
128
+
129
+ def split(self, sep: str | UserString | None = None, maxsplit: int = -1) -> ExtendedList[ExtendedString]: # type: ignore[override]
130
+ """Split into extended string parts."""
131
+ from extended_data.containers.sequences import ExtendedList
132
+
133
+ separator = None if sep is None else _coerce_string_argument(sep)
134
+ return ExtendedList(ExtendedString(part) for part in self.data.split(separator, maxsplit))
135
+
136
+ def rsplit(self, sep: str | UserString | None = None, maxsplit: int = -1) -> ExtendedList[ExtendedString]: # type: ignore[override]
137
+ """Split from the right into extended string parts."""
138
+ from extended_data.containers.sequences import ExtendedList
139
+
140
+ separator = None if sep is None else _coerce_string_argument(sep)
141
+ return ExtendedList(ExtendedString(part) for part in self.data.rsplit(separator, maxsplit))
142
+
143
+ def splitlines(self, keepends: bool = False) -> ExtendedList[ExtendedString]: # type: ignore[override]
144
+ """Split lines into extended string parts."""
145
+ from extended_data.containers.sequences import ExtendedList
146
+
147
+ return ExtendedList(ExtendedString(part) for part in self.data.splitlines(keepends))
148
+
149
+ def partition(self, sep: str | UserString) -> ExtendedTuple[ExtendedString]: # type: ignore[override]
150
+ """Partition into extended string parts."""
151
+ from extended_data.containers.sequences import ExtendedTuple
152
+
153
+ return ExtendedTuple(ExtendedString(part) for part in self.data.partition(_coerce_string_argument(sep)))
154
+
155
+ def rpartition(self, sep: str | UserString) -> ExtendedTuple[ExtendedString]: # type: ignore[override]
156
+ """Partition from the right into extended string parts."""
157
+ from extended_data.containers.sequences import ExtendedTuple
158
+
159
+ return ExtendedTuple(ExtendedString(part) for part in self.data.rpartition(_coerce_string_argument(sep)))
160
+
161
+ def join(self, seq: Iterable[str | UserString]) -> ExtendedString: # type: ignore[override]
162
+ """Join string-like values into an extended string."""
163
+ return ExtendedString(self.data.join(_coerce_string_argument(item) for item in seq))
164
+
165
+ def is_partial_match(self, other: str | None, *, check_prefix_only: bool = False) -> bool:
166
+ """Return whether this string partially matches another string."""
167
+ return primitive_matching.is_partial_match(self.data, other, check_prefix_only=check_prefix_only)
168
+
169
+ def is_non_empty_match(self, other: object) -> bool:
170
+ """Return whether this string matches another non-empty string value."""
171
+ return primitive_matching.is_non_empty_match(self.data, other)
172
+
173
+ def is_url(self) -> bool:
174
+ """Return whether the string is a URL."""
175
+ return is_url(self.data)
176
+
177
+ def to_bool(self, *, raise_on_error: bool = False) -> bool | None:
178
+ """Return a boolean parsed from the string."""
179
+ return string_to_bool(self.data, raise_on_error=raise_on_error)
180
+
181
+ def to_float(self, *, raise_on_error: bool = False) -> float | None:
182
+ """Return a float parsed from the string."""
183
+ return string_to_float(self.data, raise_on_error=raise_on_error)
184
+
185
+ def to_int(self, *, raise_on_error: bool = False) -> int | None:
186
+ """Return an integer parsed from the string."""
187
+ return string_to_int(self.data, raise_on_error=raise_on_error)
188
+
189
+ def to_path(self, *, raise_on_error: bool = False) -> Path | None:
190
+ """Return a path parsed from the string."""
191
+ return string_to_path(self.data, raise_on_error=raise_on_error)
192
+
193
+ def to_date(self, *, raise_on_error: bool = False) -> datetime.date | None:
194
+ """Return a date parsed from the string."""
195
+ return string_to_date(self.data, raise_on_error=raise_on_error)
196
+
197
+ def to_datetime(self, *, raise_on_error: bool = False) -> datetime.datetime | None:
198
+ """Return a datetime parsed from the string."""
199
+ return string_to_datetime(self.data, raise_on_error=raise_on_error)
200
+
201
+ def to_time(self, *, raise_on_error: bool = False) -> datetime.time | None:
202
+ """Return a time parsed from the string."""
203
+ return string_to_time(self.data, raise_on_error=raise_on_error)
204
+
205
+ def reconstruct_special_type(self, *, fail_silently: bool = False) -> object:
206
+ """Return the string reconstructed as a known scalar or structured value."""
207
+ from extended_data.containers.factory import extend_data
208
+
209
+ return extend_data(reconstruct_special_type(self.data, fail_silently=fail_silently))
210
+
211
+ def decode_json(self, *, as_extended: bool = True) -> Any:
212
+ """Decode this JSON string, promoting structured values by default."""
213
+ from extended_data.containers.factory import extend_data
214
+ from extended_data.primitives.formats.json import decode_json
215
+
216
+ decoded = decode_json(self.data)
217
+ return extend_data(decoded) if as_extended else decoded
218
+
219
+ def decode_yaml(self, *, as_extended: bool = True) -> Any:
220
+ """Decode this YAML string, promoting structured values by default."""
221
+ from extended_data.containers.factory import extend_data
222
+ from extended_data.primitives.formats.yaml import decode_yaml
223
+
224
+ decoded = decode_yaml(self.data)
225
+ return extend_data(decoded) if as_extended else decoded
226
+
227
+ def decode_toml(self, *, as_extended: bool = True) -> Any:
228
+ """Decode this TOML string, promoting structured values by default."""
229
+ from extended_data.containers.factory import extend_data
230
+ from extended_data.primitives.formats.toml import decode_toml
231
+
232
+ decoded = decode_toml(self.data)
233
+ return extend_data(decoded) if as_extended else decoded
234
+
235
+ def decode_hcl2(self, *, as_extended: bool = True) -> Any:
236
+ """Decode this HCL2 string, promoting structured values by default."""
237
+ from extended_data.containers.factory import extend_data
238
+ from extended_data.primitives.formats.hcl import decode_hcl2
239
+
240
+ decoded = decode_hcl2(self.data)
241
+ return extend_data(decoded) if as_extended else decoded
242
+
243
+ def encode_base64(self, *, wrap_raw_data: bool = True) -> ExtendedString:
244
+ """Return this string encoded as Base64."""
245
+ from extended_data.io.base64 import base64_encode
246
+
247
+ return ExtendedString(base64_encode(self.data, wrap_raw_data=wrap_raw_data))
248
+
249
+ def decode_base64(
250
+ self,
251
+ unwrap_raw_data: bool = True,
252
+ encoding: str = "yaml",
253
+ *,
254
+ as_extended: bool = True,
255
+ ) -> Any:
256
+ """Decode this Base64 string, promoting structured values by default."""
257
+ from extended_data.io.base64 import base64_decode
258
+
259
+ return base64_decode(
260
+ self.data,
261
+ unwrap_raw_data=unwrap_raw_data,
262
+ encoding=encoding,
263
+ as_extended=as_extended,
264
+ )
265
+
266
+ def to_export_safe(self, *, export_to_yaml: bool = False) -> Any:
267
+ """Return this value converted to export-safe primitive data."""
268
+ from extended_data.io.exporters import make_raw_data_export_safe
269
+
270
+ return make_raw_data_export_safe(self.data, export_to_yaml=export_to_yaml)
271
+
272
+ def wrap_for_export(self, allow_encoding: bool | str = True, **format_opts: Any) -> str:
273
+ """Return this value wrapped as an encoded export string."""
274
+ from extended_data.io.exporters import wrap_raw_data_for_export
275
+
276
+ return wrap_raw_data_for_export(self.data, allow_encoding=allow_encoding, **format_opts)