classes.c32_pool
1import random 2import unicodedata 3import colorama 4import regex 5from inspect import cleandoc 6from typing import Literal 7from loguru import logger 8from icecream import ic 9 10from lib import files, helpers, content 11 12DEBUG = False 13 14 15class KPool: 16 """ 17 A pool of items with optional shuffling and case transformation utilities. 18 """ 19 20 def __init__( 21 self, items: list[str], shuffle=True, defaultCase: content.TextCase = None 22 ) -> None: 23 """ 24 Initialize the KPool instance. 25 26 Args: 27 items: List of items to pool. 28 shuffle: Randomize items on initialization. Defaults to True. 29 defaultCase: Default `content.TextCase` for items. If None, no case change is applied. 30 """ 31 if shuffle: 32 random.shuffle(items) 33 34 self._shuffle: bool = shuffle 35 36 self._items = items 37 """Internal copy of items without case change""" 38 39 self.items = content.changeCase(items, defaultCase) 40 """Items with case possibly changed (upon initialization)""" 41 42 @staticmethod 43 def fromFile( 44 filePath: str = "/usr/share/dict/words", 45 shuffle=True, 46 defaultCase: content.TextCase = "title", 47 ) -> "KPool": 48 """ 49 Create a KPool instance from a text file, where each line is an item. 50 51 Args: 52 filePath: Path to the text file. Defaults to a common dictionary file. 53 shuffle: Randomize items on initialization. Defaults to True. 54 defaultCase: Default `content.TextCase` for items. If None, no case change is applied. 55 56 Returns: 57 KPool instance with items from the file. 58 """ 59 from classes import KTextCleaner 60 61 items = files.readFileLines(filePath) 62 # ? Sanitize items to remove inappropriate content 63 items = KTextCleaner().sanitizeForbidden(items) 64 return KPool(items, shuffle=shuffle, defaultCase=defaultCase) 65 66 def __str__(self) -> str: 67 """Returns a human-readable string representation of the KPool.""" 68 return cleandoc( 69 f""" 70 {colorama.Fore.BLACK}{colorama.Back.LIGHTCYAN_EX}KPool{colorama.Style.RESET_ALL} {helpers.previewList(self.items)} 71 """ 72 ) 73 74 def __len__(self) -> int: 75 """Returns the number of items in the pool.""" 76 return len(self.items) 77 78 def __getitem__(self, index): 79 """ 80 Make subscriptable. Allows access to items using square brackets. 81 82 Args: 83 index: Index of the item. 84 85 Returns: 86 Item at the given index. 87 """ 88 return self.items[index] 89 90 def getRandomItem(self, alterCase: content.TextCase = None) -> str: 91 """ 92 Get a random item from the pool, with optional case alteration. 93 94 Args: 95 alterCase: Change text case for a single item. 96 97 Returns: 98 Random item from the pool, possibly with altered case. 99 """ 100 if not self.items: 101 return "Blank" 102 103 item = random.choice(self.items) 104 if isinstance(alterCase, str): 105 item = content.changeCase(item, alterCase) 106 107 self._removeItem(item) 108 return item 109 110 def getItemByWidth( 111 self, 112 width: int, 113 alterCase: content.TextCase = None, 114 avoidShape: content.WordShape = None, 115 threshold: float = 0.995, 116 ) -> str: 117 """ 118 Get an item matching the specified width, with optional case and shape filtering. 119 120 Args: 121 width: Target width for the item. 122 alterCase: Change text case for a single phrase. 123 avoidShape: Exclude items with this shape. 124 threshold: Matching threshold for the width. 125 Returns: 126 Item matching the criteria, or `Blank` if none found. 127 """ 128 if isinstance(alterCase, str): 129 items = content.changeCase(self.items, alterCase) 130 else: 131 items = self.items 132 133 if avoidShape: 134 items = content.filterByShape(items, avoidShape) 135 136 if not items: 137 return "Blank" 138 139 if self._shuffle: 140 random.shuffle(items) # Shuffle on each run 141 142 match = content.getStringForWidth(items, width, threshold) 143 144 self._removeItem(match) 145 return match 146 147 def getItemForDimensions( 148 self, 149 dimensions: tuple[float, float], 150 threshold: float = 0.85, 151 optimalLastLineRatio: float = 1, 152 ): 153 """Get an item matching the specified dimensions, with optional threshold and last line ratio. See `lib.content.getStringForDimensions` for details.""" 154 match = content.getStringForDimensions( 155 self.items, 156 dimensions, 157 threshold=threshold, 158 optimalLastLineRatio=optimalLastLineRatio, 159 ) 160 self._removeItem(match) 161 return match 162 163 def setCase(self, case: content.TextCase): 164 """ 165 Change the text case of all items at the instance level. 166 167 Args: 168 case: Desired text case. 169 170 Returns: 171 The modified KPool instance. 172 """ 173 self.items = content.changeCase(self.items, case) 174 return self 175 176 def filterPrefix(self, pattern: str | list[str], mode: helpers.Strategy = "pick"): 177 """ 178 Return a filtered instance with items (not) starting with the given pattern(s). 179 180 Args: 181 pattern: Single string or multiple patterns. 182 mode: Which filtering strategy to use: 183 - `pick`: include items of pattern only 184 - `omit`: remove pattern from items 185 186 Returns: 187 Filtered KPool instance. 188 """ 189 190 def _process(string: str): 191 def _isMatch(item: str) -> bool: 192 found = item.startswith(string) 193 return found if mode == "pick" else not found 194 195 return [ 196 item for item in self.items if _isMatch(item) 197 ] # Process items to preserve textCase 198 199 intersected = helpers.intersect( 200 [_process(p) for p in helpers.coerceList(pattern)] 201 ) 202 return KPool( 203 intersected, 204 shuffle=self._shuffle, 205 ) 206 207 def filterTokens(self, tokens: list[content.CharacterToken] = ["word", "nonword"]): 208 """ 209 Return a filtered instance with items of certain Unicode tokens. See `lib.content.filterByTokens`. 210 211 Args: 212 tokens: Tokens to filter by. 213 214 Returns: 215 Filtered KPool instance. 216 """ 217 return KPool( 218 content.filterByTokens(self.items, tokens), shuffle=self._shuffle 219 ) # Process items to preserve textCase 220 221 def filterWithRegex(self, pattern: str): 222 """ 223 Return a filtered KPool instance with items matching the given regex pattern. 224 225 Examples: 226 - `[^bdfhkl]+` => filter ascenders 227 - `[^Qgjpqy]+` => filter descenders 228 229 Args: 230 pattern: Regex pattern to match. 231 232 Returns: 233 Filtered KPool instance. 234 """ 235 return KPool( 236 list(filter(lambda item: regex.search(f"^{pattern}$", item), self.items)), 237 shuffle=self._shuffle, 238 ) # Process items to preserve textCase 239 240 def shuffle(self) -> "KPool": 241 """Shuffle the items in place and return self.""" 242 random.shuffle(self.items) 243 return self 244 245 def stripChars(self, chars: str = ".,:;-+−'’") -> None: 246 """ 247 Remove specified characters from all items. 248 249 Args: 250 chars: Characters to strip. Defaults to commonly used symbols and punctuation. 251 """ 252 self.items = [item.strip(chars) for item in self.items] 253 254 def prioritize(self, mode: Literal["accented"] = "accented") -> "KPool": 255 """Reorder items in place by priority mode. 256 257 Current modes: 258 - accented: items containing accented/diacritic characters come first. 259 """ 260 if mode != "accented": 261 raise ValueError(f"Unsupported prioritize mode: {mode}") 262 263 def _hasAccents(item: str) -> bool: 264 normalized = unicodedata.normalize("NFD", item) 265 return any(unicodedata.combining(char) for char in normalized) 266 267 # Stable sort keeps relative order within each priority group. 268 self.items.sort(key=lambda item: not _hasAccents(item)) 269 return self 270 271 def _removeItem(self, match: str): 272 """ 273 Remove an item by value (case-insensitive). 274 275 Args: 276 match: Item to remove. 277 278 Returns: 279 The modified KPool instance. 280 """ 281 poolLen = f"{len(self.items)}\t" 282 try: 283 if DEBUG: 284 logger.debug("[{} About to remove]\t{}", poolLen, match) 285 matchInsensitive = match.casefold() 286 itemsInsensitive = [item.casefold() for item in self.items] 287 index = itemsInsensitive.index(matchInsensitive) 288 value = self.items[index] 289 if DEBUG: 290 logger.debug("[{} Removing]\t{}", poolLen, value) 291 self.items.pop(index) 292 except ValueError as e: 293 logger.warning("[{} Error removing item]\t{}: {}", poolLen, match, e) 294 return self
16class KPool: 17 """ 18 A pool of items with optional shuffling and case transformation utilities. 19 """ 20 21 def __init__( 22 self, items: list[str], shuffle=True, defaultCase: content.TextCase = None 23 ) -> None: 24 """ 25 Initialize the KPool instance. 26 27 Args: 28 items: List of items to pool. 29 shuffle: Randomize items on initialization. Defaults to True. 30 defaultCase: Default `content.TextCase` for items. If None, no case change is applied. 31 """ 32 if shuffle: 33 random.shuffle(items) 34 35 self._shuffle: bool = shuffle 36 37 self._items = items 38 """Internal copy of items without case change""" 39 40 self.items = content.changeCase(items, defaultCase) 41 """Items with case possibly changed (upon initialization)""" 42 43 @staticmethod 44 def fromFile( 45 filePath: str = "/usr/share/dict/words", 46 shuffle=True, 47 defaultCase: content.TextCase = "title", 48 ) -> "KPool": 49 """ 50 Create a KPool instance from a text file, where each line is an item. 51 52 Args: 53 filePath: Path to the text file. Defaults to a common dictionary file. 54 shuffle: Randomize items on initialization. Defaults to True. 55 defaultCase: Default `content.TextCase` for items. If None, no case change is applied. 56 57 Returns: 58 KPool instance with items from the file. 59 """ 60 from classes import KTextCleaner 61 62 items = files.readFileLines(filePath) 63 # ? Sanitize items to remove inappropriate content 64 items = KTextCleaner().sanitizeForbidden(items) 65 return KPool(items, shuffle=shuffle, defaultCase=defaultCase) 66 67 def __str__(self) -> str: 68 """Returns a human-readable string representation of the KPool.""" 69 return cleandoc( 70 f""" 71 {colorama.Fore.BLACK}{colorama.Back.LIGHTCYAN_EX}KPool{colorama.Style.RESET_ALL} {helpers.previewList(self.items)} 72 """ 73 ) 74 75 def __len__(self) -> int: 76 """Returns the number of items in the pool.""" 77 return len(self.items) 78 79 def __getitem__(self, index): 80 """ 81 Make subscriptable. Allows access to items using square brackets. 82 83 Args: 84 index: Index of the item. 85 86 Returns: 87 Item at the given index. 88 """ 89 return self.items[index] 90 91 def getRandomItem(self, alterCase: content.TextCase = None) -> str: 92 """ 93 Get a random item from the pool, with optional case alteration. 94 95 Args: 96 alterCase: Change text case for a single item. 97 98 Returns: 99 Random item from the pool, possibly with altered case. 100 """ 101 if not self.items: 102 return "Blank" 103 104 item = random.choice(self.items) 105 if isinstance(alterCase, str): 106 item = content.changeCase(item, alterCase) 107 108 self._removeItem(item) 109 return item 110 111 def getItemByWidth( 112 self, 113 width: int, 114 alterCase: content.TextCase = None, 115 avoidShape: content.WordShape = None, 116 threshold: float = 0.995, 117 ) -> str: 118 """ 119 Get an item matching the specified width, with optional case and shape filtering. 120 121 Args: 122 width: Target width for the item. 123 alterCase: Change text case for a single phrase. 124 avoidShape: Exclude items with this shape. 125 threshold: Matching threshold for the width. 126 Returns: 127 Item matching the criteria, or `Blank` if none found. 128 """ 129 if isinstance(alterCase, str): 130 items = content.changeCase(self.items, alterCase) 131 else: 132 items = self.items 133 134 if avoidShape: 135 items = content.filterByShape(items, avoidShape) 136 137 if not items: 138 return "Blank" 139 140 if self._shuffle: 141 random.shuffle(items) # Shuffle on each run 142 143 match = content.getStringForWidth(items, width, threshold) 144 145 self._removeItem(match) 146 return match 147 148 def getItemForDimensions( 149 self, 150 dimensions: tuple[float, float], 151 threshold: float = 0.85, 152 optimalLastLineRatio: float = 1, 153 ): 154 """Get an item matching the specified dimensions, with optional threshold and last line ratio. See `lib.content.getStringForDimensions` for details.""" 155 match = content.getStringForDimensions( 156 self.items, 157 dimensions, 158 threshold=threshold, 159 optimalLastLineRatio=optimalLastLineRatio, 160 ) 161 self._removeItem(match) 162 return match 163 164 def setCase(self, case: content.TextCase): 165 """ 166 Change the text case of all items at the instance level. 167 168 Args: 169 case: Desired text case. 170 171 Returns: 172 The modified KPool instance. 173 """ 174 self.items = content.changeCase(self.items, case) 175 return self 176 177 def filterPrefix(self, pattern: str | list[str], mode: helpers.Strategy = "pick"): 178 """ 179 Return a filtered instance with items (not) starting with the given pattern(s). 180 181 Args: 182 pattern: Single string or multiple patterns. 183 mode: Which filtering strategy to use: 184 - `pick`: include items of pattern only 185 - `omit`: remove pattern from items 186 187 Returns: 188 Filtered KPool instance. 189 """ 190 191 def _process(string: str): 192 def _isMatch(item: str) -> bool: 193 found = item.startswith(string) 194 return found if mode == "pick" else not found 195 196 return [ 197 item for item in self.items if _isMatch(item) 198 ] # Process items to preserve textCase 199 200 intersected = helpers.intersect( 201 [_process(p) for p in helpers.coerceList(pattern)] 202 ) 203 return KPool( 204 intersected, 205 shuffle=self._shuffle, 206 ) 207 208 def filterTokens(self, tokens: list[content.CharacterToken] = ["word", "nonword"]): 209 """ 210 Return a filtered instance with items of certain Unicode tokens. See `lib.content.filterByTokens`. 211 212 Args: 213 tokens: Tokens to filter by. 214 215 Returns: 216 Filtered KPool instance. 217 """ 218 return KPool( 219 content.filterByTokens(self.items, tokens), shuffle=self._shuffle 220 ) # Process items to preserve textCase 221 222 def filterWithRegex(self, pattern: str): 223 """ 224 Return a filtered KPool instance with items matching the given regex pattern. 225 226 Examples: 227 - `[^bdfhkl]+` => filter ascenders 228 - `[^Qgjpqy]+` => filter descenders 229 230 Args: 231 pattern: Regex pattern to match. 232 233 Returns: 234 Filtered KPool instance. 235 """ 236 return KPool( 237 list(filter(lambda item: regex.search(f"^{pattern}$", item), self.items)), 238 shuffle=self._shuffle, 239 ) # Process items to preserve textCase 240 241 def shuffle(self) -> "KPool": 242 """Shuffle the items in place and return self.""" 243 random.shuffle(self.items) 244 return self 245 246 def stripChars(self, chars: str = ".,:;-+−'’") -> None: 247 """ 248 Remove specified characters from all items. 249 250 Args: 251 chars: Characters to strip. Defaults to commonly used symbols and punctuation. 252 """ 253 self.items = [item.strip(chars) for item in self.items] 254 255 def prioritize(self, mode: Literal["accented"] = "accented") -> "KPool": 256 """Reorder items in place by priority mode. 257 258 Current modes: 259 - accented: items containing accented/diacritic characters come first. 260 """ 261 if mode != "accented": 262 raise ValueError(f"Unsupported prioritize mode: {mode}") 263 264 def _hasAccents(item: str) -> bool: 265 normalized = unicodedata.normalize("NFD", item) 266 return any(unicodedata.combining(char) for char in normalized) 267 268 # Stable sort keeps relative order within each priority group. 269 self.items.sort(key=lambda item: not _hasAccents(item)) 270 return self 271 272 def _removeItem(self, match: str): 273 """ 274 Remove an item by value (case-insensitive). 275 276 Args: 277 match: Item to remove. 278 279 Returns: 280 The modified KPool instance. 281 """ 282 poolLen = f"{len(self.items)}\t" 283 try: 284 if DEBUG: 285 logger.debug("[{} About to remove]\t{}", poolLen, match) 286 matchInsensitive = match.casefold() 287 itemsInsensitive = [item.casefold() for item in self.items] 288 index = itemsInsensitive.index(matchInsensitive) 289 value = self.items[index] 290 if DEBUG: 291 logger.debug("[{} Removing]\t{}", poolLen, value) 292 self.items.pop(index) 293 except ValueError as e: 294 logger.warning("[{} Error removing item]\t{}: {}", poolLen, match, e) 295 return self
A pool of items with optional shuffling and case transformation utilities.
21 def __init__( 22 self, items: list[str], shuffle=True, defaultCase: content.TextCase = None 23 ) -> None: 24 """ 25 Initialize the KPool instance. 26 27 Args: 28 items: List of items to pool. 29 shuffle: Randomize items on initialization. Defaults to True. 30 defaultCase: Default `content.TextCase` for items. If None, no case change is applied. 31 """ 32 if shuffle: 33 random.shuffle(items) 34 35 self._shuffle: bool = shuffle 36 37 self._items = items 38 """Internal copy of items without case change""" 39 40 self.items = content.changeCase(items, defaultCase) 41 """Items with case possibly changed (upon initialization)"""
Initialize the KPool instance.
Arguments:
- items: List of items to pool.
- shuffle: Randomize items on initialization. Defaults to True.
- defaultCase: Default
content.TextCasefor items. If None, no case change is applied.
43 @staticmethod 44 def fromFile( 45 filePath: str = "/usr/share/dict/words", 46 shuffle=True, 47 defaultCase: content.TextCase = "title", 48 ) -> "KPool": 49 """ 50 Create a KPool instance from a text file, where each line is an item. 51 52 Args: 53 filePath: Path to the text file. Defaults to a common dictionary file. 54 shuffle: Randomize items on initialization. Defaults to True. 55 defaultCase: Default `content.TextCase` for items. If None, no case change is applied. 56 57 Returns: 58 KPool instance with items from the file. 59 """ 60 from classes import KTextCleaner 61 62 items = files.readFileLines(filePath) 63 # ? Sanitize items to remove inappropriate content 64 items = KTextCleaner().sanitizeForbidden(items) 65 return KPool(items, shuffle=shuffle, defaultCase=defaultCase)
Create a KPool instance from a text file, where each line is an item.
Arguments:
- filePath: Path to the text file. Defaults to a common dictionary file.
- shuffle: Randomize items on initialization. Defaults to True.
- defaultCase: Default
content.TextCasefor items. If None, no case change is applied.
Returns:
KPool instance with items from the file.
91 def getRandomItem(self, alterCase: content.TextCase = None) -> str: 92 """ 93 Get a random item from the pool, with optional case alteration. 94 95 Args: 96 alterCase: Change text case for a single item. 97 98 Returns: 99 Random item from the pool, possibly with altered case. 100 """ 101 if not self.items: 102 return "Blank" 103 104 item = random.choice(self.items) 105 if isinstance(alterCase, str): 106 item = content.changeCase(item, alterCase) 107 108 self._removeItem(item) 109 return item
Get a random item from the pool, with optional case alteration.
Arguments:
- alterCase: Change text case for a single item.
Returns:
Random item from the pool, possibly with altered case.
111 def getItemByWidth( 112 self, 113 width: int, 114 alterCase: content.TextCase = None, 115 avoidShape: content.WordShape = None, 116 threshold: float = 0.995, 117 ) -> str: 118 """ 119 Get an item matching the specified width, with optional case and shape filtering. 120 121 Args: 122 width: Target width for the item. 123 alterCase: Change text case for a single phrase. 124 avoidShape: Exclude items with this shape. 125 threshold: Matching threshold for the width. 126 Returns: 127 Item matching the criteria, or `Blank` if none found. 128 """ 129 if isinstance(alterCase, str): 130 items = content.changeCase(self.items, alterCase) 131 else: 132 items = self.items 133 134 if avoidShape: 135 items = content.filterByShape(items, avoidShape) 136 137 if not items: 138 return "Blank" 139 140 if self._shuffle: 141 random.shuffle(items) # Shuffle on each run 142 143 match = content.getStringForWidth(items, width, threshold) 144 145 self._removeItem(match) 146 return match
Get an item matching the specified width, with optional case and shape filtering.
Arguments:
- width: Target width for the item.
- alterCase: Change text case for a single phrase.
- avoidShape: Exclude items with this shape.
- threshold: Matching threshold for the width.
Returns:
Item matching the criteria, or
Blankif none found.
148 def getItemForDimensions( 149 self, 150 dimensions: tuple[float, float], 151 threshold: float = 0.85, 152 optimalLastLineRatio: float = 1, 153 ): 154 """Get an item matching the specified dimensions, with optional threshold and last line ratio. See `lib.content.getStringForDimensions` for details.""" 155 match = content.getStringForDimensions( 156 self.items, 157 dimensions, 158 threshold=threshold, 159 optimalLastLineRatio=optimalLastLineRatio, 160 ) 161 self._removeItem(match) 162 return match
Get an item matching the specified dimensions, with optional threshold and last line ratio. See lib.content.getStringForDimensions for details.
164 def setCase(self, case: content.TextCase): 165 """ 166 Change the text case of all items at the instance level. 167 168 Args: 169 case: Desired text case. 170 171 Returns: 172 The modified KPool instance. 173 """ 174 self.items = content.changeCase(self.items, case) 175 return self
Change the text case of all items at the instance level.
Arguments:
- case: Desired text case.
Returns:
The modified KPool instance.
177 def filterPrefix(self, pattern: str | list[str], mode: helpers.Strategy = "pick"): 178 """ 179 Return a filtered instance with items (not) starting with the given pattern(s). 180 181 Args: 182 pattern: Single string or multiple patterns. 183 mode: Which filtering strategy to use: 184 - `pick`: include items of pattern only 185 - `omit`: remove pattern from items 186 187 Returns: 188 Filtered KPool instance. 189 """ 190 191 def _process(string: str): 192 def _isMatch(item: str) -> bool: 193 found = item.startswith(string) 194 return found if mode == "pick" else not found 195 196 return [ 197 item for item in self.items if _isMatch(item) 198 ] # Process items to preserve textCase 199 200 intersected = helpers.intersect( 201 [_process(p) for p in helpers.coerceList(pattern)] 202 ) 203 return KPool( 204 intersected, 205 shuffle=self._shuffle, 206 )
Return a filtered instance with items (not) starting with the given pattern(s).
Arguments:
- pattern: Single string or multiple patterns.
- mode: Which filtering strategy to use:
pick: include items of pattern onlyomit: remove pattern from items
Returns:
Filtered KPool instance.
208 def filterTokens(self, tokens: list[content.CharacterToken] = ["word", "nonword"]): 209 """ 210 Return a filtered instance with items of certain Unicode tokens. See `lib.content.filterByTokens`. 211 212 Args: 213 tokens: Tokens to filter by. 214 215 Returns: 216 Filtered KPool instance. 217 """ 218 return KPool( 219 content.filterByTokens(self.items, tokens), shuffle=self._shuffle 220 ) # Process items to preserve textCase
Return a filtered instance with items of certain Unicode tokens. See lib.content.filterByTokens.
Arguments:
- tokens: Tokens to filter by.
Returns:
Filtered KPool instance.
222 def filterWithRegex(self, pattern: str): 223 """ 224 Return a filtered KPool instance with items matching the given regex pattern. 225 226 Examples: 227 - `[^bdfhkl]+` => filter ascenders 228 - `[^Qgjpqy]+` => filter descenders 229 230 Args: 231 pattern: Regex pattern to match. 232 233 Returns: 234 Filtered KPool instance. 235 """ 236 return KPool( 237 list(filter(lambda item: regex.search(f"^{pattern}$", item), self.items)), 238 shuffle=self._shuffle, 239 ) # Process items to preserve textCase
Return a filtered KPool instance with items matching the given regex pattern.
Examples:
[^bdfhkl]+=> filter ascenders[^Qgjpqy]+=> filter descenders
Arguments:
- pattern: Regex pattern to match.
Returns:
Filtered KPool instance.
241 def shuffle(self) -> "KPool": 242 """Shuffle the items in place and return self.""" 243 random.shuffle(self.items) 244 return self
Shuffle the items in place and return self.
246 def stripChars(self, chars: str = ".,:;-+−'’") -> None: 247 """ 248 Remove specified characters from all items. 249 250 Args: 251 chars: Characters to strip. Defaults to commonly used symbols and punctuation. 252 """ 253 self.items = [item.strip(chars) for item in self.items]
Remove specified characters from all items.
Arguments:
- chars: Characters to strip. Defaults to commonly used symbols and punctuation.
255 def prioritize(self, mode: Literal["accented"] = "accented") -> "KPool": 256 """Reorder items in place by priority mode. 257 258 Current modes: 259 - accented: items containing accented/diacritic characters come first. 260 """ 261 if mode != "accented": 262 raise ValueError(f"Unsupported prioritize mode: {mode}") 263 264 def _hasAccents(item: str) -> bool: 265 normalized = unicodedata.normalize("NFD", item) 266 return any(unicodedata.combining(char) for char in normalized) 267 268 # Stable sort keeps relative order within each priority group. 269 self.items.sort(key=lambda item: not _hasAccents(item)) 270 return self
Reorder items in place by priority mode.
Current modes:
- accented: items containing accented/diacritic characters come first.