from __future__ import annotations
import contextlib
from typing import Any, Iterable, overload
[docs]class StrList:
"""
String List Class.
.. versionadded:: 0.41.0
"""
[docs] def __init__(self, strings: Iterable[str] | None = None, sep: str = ";"):
"""
Constructor
Args:
strings (Iterable[str], optional): Iterable String such as a list or a tuple. If a str in passed int the each char become an item. Defaults to None.
sep (str, optional): _description_. Defaults to ";".
"""
if strings is None:
strings = []
else:
strings = list(strings)
self._strings = strings
self._sep = sep
self._iter_index = 0
self._indent = 0
self._indent_str = " "
# region Methods
[docs] def append(self, value: str = "", no_indent: bool = False) -> StrList:
"""
Add a string to the list
Args:
value (str, optional): String to add. Defaults to "".
no_indent (bool, optional): If True, no indent is added. Defaults to False.
Returns:
StrList: Self.
"""
if self._indent > 0 and not no_indent:
value = self._get_indent_str() + value
self._strings.append(value)
return self
[docs] def remove(self, value: str) -> StrList:
"""
Remove a string from the list
Args:
value (str): String to remove.
Returns:
StrList: Self.
"""
self._strings.remove(value)
return self
[docs] def to_string(self) -> str:
"""Convert list to string."""
return str(self)
[docs] def clear(self) -> StrList:
"""Clear the list."""
self._strings.clear()
return self
[docs] def copy(self) -> StrList:
"""Copy the list."""
return StrList(strings=self._strings.copy(), sep=self._sep)
[docs] def extend(self, strings: Iterable[str], no_indent: bool = False) -> StrList:
"""
Extend the list.
Args:
strings (Iterable[str]): Strings to add.
no_indent (bool, optional): If True, no indent is added. Defaults to False.
"""
if self._indent > 0 and not no_indent:
strings = [self._get_indent_str() + string for string in strings]
self._strings.extend(strings)
return self
[docs] def insert(self, index: int, value: str, no_indent: bool = False) -> StrList:
"""
Insert a value into the list.
Args:
index (int): Index to insert the value.
value (str): Value to insert.
no_indent (bool, optional): If True, no indent is added. Defaults to False.
"""
if self._indent > 0 and not no_indent:
value = self._get_indent_str() + value
self._strings.insert(index, value)
return self
[docs] def pop(self, index: int) -> str:
"""Pop a value from the list."""
return self._strings.pop(index)
[docs] def index(self, value: str) -> int:
"""Get the index of a value."""
return self._strings.index(value)
[docs] def count(self, value: str) -> int:
"""Get the count of a value."""
return self._strings.count(value)
[docs] def reverse(self) -> StrList:
"""Reverse the list."""
self._strings.reverse()
return self
[docs] def sort(self, key=None, reverse=False) -> StrList:
"""Sort the list."""
self._strings.sort(key=key, reverse=reverse)
return self
[docs] def remove_duplicates(self) -> StrList:
"""Remove duplicates from the list."""
self._strings = list(dict.fromkeys(self._strings))
return self
# region Indent Methods
[docs] @contextlib.contextmanager
def indented(self):
"""
Context Manager. Increase the indent.
Example:
.. code-block:: python
code = StrList(sep="\\n")
code.append("Sub Main")
with code.indented():
code.append('MsgBox "Hello World"')
code.append("End Sub")
"""
self.indent_amt += 1
try:
yield self
finally:
self.indent_amt -= 1
def _get_indent_str(self) -> str:
if self._indent < 1:
return ""
return self._indent_str * self._indent
[docs] def set_indent(self, value: int) -> StrList:
"""Set the indent."""
self._indent = max(0, value)
return self
[docs] def increase_indent(self) -> StrList:
"""Increase the indent."""
self._indent += 1
return self
[docs] def decrease_indent(self) -> StrList:
"""Decrease the indent."""
self._indent -= 1
self._indent = max(0, self._indent)
return self
# endregion Indent Methods
# endregion Methods
# region Dunder Methods
[docs] def __add__(self, other: StrList) -> StrList:
"""Add two lists."""
return StrList(strings=self._strings + other._strings, sep=self._sep)
[docs] def __iadd__(self, other: StrList) -> StrList:
"""Add two lists."""
self._strings += other._strings
return self
[docs] def __eq__(self, other: StrList) -> bool:
"""Check if two lists are equal."""
return self._strings == other._strings
[docs] def __contains__(self, value: str):
"""Get if the value is in the list."""
return value in self._strings
def __iter__(self):
"""Iterator for the list."""
self._iter_index = 0
length = len(self)
while self._iter_index < length:
yield self._strings[self._iter_index]
self._iter_index += 1
[docs] def __reversed__(self):
"""Reverse iterator for the list."""
self._iter_index = len(self) - 1
while self._iter_index >= 0:
yield self._strings[self._iter_index]
self._iter_index -= 1
def __str__(self):
"""Convert list to string."""
if not self._strings:
return ""
return self._sep.join(self._strings)
[docs] def __len__(self):
"""Get the length of the list."""
return len(self._strings)
def __delitem__(self, index):
"""Delete an item from the list."""
del self._strings[index]
# region __getitem__
@overload
def __getitem__(self, index: int) -> str: ...
@overload
def __getitem__(self, index: slice) -> StrList: ...
[docs] def __getitem__(self, index: int | slice) -> Any:
"""
Get an item from the list.
Supports slicing. When sliced a new StrList is returned.
"""
if isinstance(index, slice):
return type(self)(self._strings[index], self._sep)
else:
return self._strings[index]
# endregion __getitem__
def __repr__(self) -> str:
"""Get the string representation of the object."""
return f"<{self.__class__.__name__}(count={len(self._strings)}, sep={self._sep!r})?"
# endregion Dunder Methods
# region Static Methods
[docs] @staticmethod
def from_str(value: str, sep: str = ";") -> StrList:
"""Create a StrList from a string."""
return StrList(strings=value.split(sep), sep=sep)
# endregion Static Methods
# region Properties
@property
def separator(self) -> str:
"""Gets/Sets the separator."""
return self._sep
@separator.setter
def separator(self, value: str):
"""Set the separator."""
self._sep = value
@property
def indent_amt(self) -> int:
"""Get/Sets the indent amount"""
return self._indent
@indent_amt.setter
def indent_amt(self, value: int):
"""Set the indent."""
self._indent = max(0, value)
@property
def indent_str(self) -> str:
"""Gets/Sets the indent string"""
return self._indent_str
@indent_str.setter
def indent_str(self, value: str):
self._indent_str = value
# endregion Properties