swizzle 2.2.1__py3-none-any.whl → 2.3.1__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.
swizzle/__init__.py CHANGED
@@ -1,12 +1,16 @@
1
1
  # Copyright (c) 2024 Jan T. Müller <mail@jantmueller.com>
2
2
 
3
- from functools import wraps
4
- import types
5
3
  import builtins
6
4
  import sys as _sys
7
-
5
+ import types
6
+ import unicodedata
7
+ from enum import EnumType
8
+ from functools import wraps
8
9
  from keyword import iskeyword as _iskeyword
9
10
  from operator import itemgetter as _itemgetter
11
+
12
+ from .trie import Trie
13
+
10
14
  try:
11
15
  from _collections import _tuplegetter
12
16
  except ImportError:
@@ -14,11 +18,21 @@ except ImportError:
14
18
 
15
19
  _type = builtins.type
16
20
 
17
- __version__ = "2.2.1"
21
+ __version__ = "2.3.1"
18
22
 
19
23
  MISSING = object()
20
24
 
21
- def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=None, arrange_names = None, sep = None):
25
+
26
+ def swizzledtuple(
27
+ typename,
28
+ field_names,
29
+ *,
30
+ rename=False,
31
+ defaults=None,
32
+ module=None,
33
+ arrange_names=None,
34
+ sep=None,
35
+ ):
22
36
  """
23
37
  Create a custom named tuple class with swizzled attributes, allowing for rearranged field names
24
38
  and customized attribute access.
@@ -41,9 +55,13 @@ def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=
41
55
  in which fields should be arranged in the resulting named tuple. This allows for fields
42
56
  to be rearranged and, unlike standard `namedtuple`, can include duplicates. Defaults
43
57
  to the order given in `field_names`.
44
- sep (str, optional): A separator string that customizes the structure of attribute
45
- access. If provided, this sep allows attributes to be accessed by combining field
46
- names with the sep in between them. Defaults to no sep.
58
+ sep (str, optional): A separator string used to control how attribute names are constructed.
59
+ If provided, fields will be joined using this separator to create compound attribute names.
60
+ Defaults to None.
61
+
62
+ Special case: If all field names have the same length `n` after optional renaming,
63
+ and `sep` is still None, then `sep` is automatically set to `"+n"` (e.g. "+2").
64
+ This indicates that names should be split every `n` characters for improved performance.
47
65
 
48
66
  Returns:
49
67
  type: A new subclass of `tuple` with named fields and customized attribute access.
@@ -69,29 +87,41 @@ def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=
69
87
  """
70
88
 
71
89
  if isinstance(field_names, str):
72
- field_names = field_names.replace(',', ' ').split()
90
+ field_names = field_names.replace(",", " ").split()
73
91
  field_names = list(map(str, field_names))
74
92
  if arrange_names is not None:
75
93
  if isinstance(arrange_names, str):
76
- arrange_names = arrange_names.replace(',', ' ').split()
94
+ arrange_names = arrange_names.replace(",", " ").split()
77
95
  arrange_names = list(map(str, arrange_names))
78
- assert set(arrange_names) == set(field_names), 'Arrangement must contain all field names'
96
+ assert set(arrange_names) == set(
97
+ field_names
98
+ ), "Arrangement must contain all field names"
79
99
  else:
80
100
  arrange_names = field_names.copy()
81
101
 
82
-
83
102
  typename = _sys.intern(str(typename))
84
103
 
85
- _dir = dir(tuple) + ['__match_args__', '__module__', '__slots__', '_asdict', '_field_defaults', '_fields', '_make', '_replace',]
104
+ _dir = dir(tuple) + [
105
+ "__match_args__",
106
+ "__module__",
107
+ "__slots__",
108
+ "_asdict",
109
+ "_field_defaults",
110
+ "_fields",
111
+ "_make",
112
+ "_replace",
113
+ ]
86
114
  if rename:
87
115
  seen = set()
88
116
  name_newname = {}
89
117
  for index, name in enumerate(field_names):
90
- if (not name.isidentifier()
118
+ if (
119
+ not name.isidentifier()
91
120
  or _iskeyword(name)
92
121
  or name in _dir
93
- or name in seen):
94
- field_names[index] = f'_{index}'
122
+ or name in seen
123
+ ):
124
+ field_names[index] = f"_{index}"
95
125
  name_newname[name] = field_names[index]
96
126
  seen.add(name)
97
127
  for index, name in enumerate(arrange_names):
@@ -99,20 +129,24 @@ def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=
99
129
 
100
130
  for name in [typename] + field_names:
101
131
  if type(name) is not str:
102
- raise TypeError('Type names and field names must be strings')
132
+ raise TypeError("Type names and field names must be strings")
103
133
  if not name.isidentifier():
104
- raise ValueError('Type names and field names must be valid '
105
- f'identifiers: {name!r}')
134
+ raise ValueError(
135
+ "Type names and field names must be valid " f"identifiers: {name!r}"
136
+ )
106
137
  if _iskeyword(name):
107
- raise ValueError('Type names and field names cannot be a '
108
- f'keyword: {name!r}')
138
+ raise ValueError(
139
+ "Type names and field names cannot be a " f"keyword: {name!r}"
140
+ )
109
141
  seen = set()
110
142
  for name in field_names:
111
- if name in _dir and not rename:
112
- raise ValueError('Field names cannot be an attribute name which would shadow the namedtuple methods or attributes'
113
- f'{name!r}')
143
+ if name in _dir:
144
+ raise ValueError(
145
+ "Field names cannot be an attribute name which would shadow the namedtuple methods or attributes"
146
+ f"{name!r}"
147
+ )
114
148
  if name in seen:
115
- raise ValueError(f'Encountered duplicate field name: {name!r}')
149
+ raise ValueError(f"Encountered duplicate field name: {name!r}")
116
150
  seen.add(name)
117
151
 
118
152
  arrange_indices = [field_names.index(name) for name in arrange_names]
@@ -128,29 +162,30 @@ def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=
128
162
  if defaults is not None:
129
163
  defaults = tuple(defaults)
130
164
  if len(defaults) > len(field_names):
131
- raise TypeError('Got more default values than field names')
132
- field_defaults = dict(reversed(list(zip(reversed(field_names),
133
- reversed(defaults)))))
165
+ raise TypeError("Got more default values than field names")
166
+ field_defaults = dict(
167
+ reversed(list(zip(reversed(field_names), reversed(defaults))))
168
+ )
134
169
 
135
170
  field_names = tuple(map(_sys.intern, field_names))
136
171
  arrange_names = tuple(map(_sys.intern, arrange_names))
137
172
  num_fields = len(field_names)
138
173
  num_arrange_fields = len(arrange_names)
139
- arg_list = ', '.join(field_names)
174
+ arg_list = ", ".join(field_names)
140
175
  if num_fields == 1:
141
- arg_list += ','
142
- repr_fmt = '(' + ', '.join(f'{name}=%r' for name in arrange_names) + ')'
176
+ arg_list += ","
177
+ repr_fmt = "(" + ", ".join(f"{name}=%r" for name in arrange_names) + ")"
143
178
  _dict, _tuple, _len, _map, _zip = dict, tuple, len, map, zip
144
179
 
145
180
  namespace = {
146
- '_tuple_new': tuple_new,
147
- '__builtins__': {},
148
- '__name__': f'namedtuple_{typename}',
181
+ "_tuple_new": tuple_new,
182
+ "__builtins__": {},
183
+ "__name__": f"swizzledtuple_{typename}",
149
184
  }
150
- code = f'lambda _cls, {arg_list}: _tuple_new(_cls, ({arg_list}))'
185
+ code = f"lambda _cls, {arg_list}: _tuple_new(_cls, ({arg_list}))"
151
186
  __new__ = eval(code, namespace)
152
- __new__.__name__ = '__new__'
153
- __new__.__doc__ = f'Create new instance of {typename}({arg_list})'
187
+ __new__.__name__ = "__new__"
188
+ __new__.__doc__ = f"Create new instance of {typename}({arg_list})"
154
189
  if defaults is not None:
155
190
  __new__.__defaults__ = defaults
156
191
 
@@ -158,11 +193,14 @@ def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=
158
193
  def _make(cls, iterable):
159
194
  result = tuple_new(cls, iterable)
160
195
  if _len(result) != num_arrange_fields:
161
- raise TypeError(f'Expected {num_arrange_fields} arguments, got {len(result)}')
196
+ raise TypeError(
197
+ f"Expected {num_arrange_fields} arguments, got {len(result)}"
198
+ )
162
199
  return result
163
200
 
164
- _make.__func__.__doc__ = (f'Make a new {typename} object from a sequence '
165
- 'or iterable')
201
+ _make.__func__.__doc__ = (
202
+ f"Make a new {typename} object from a sequence " "or iterable"
203
+ )
166
204
 
167
205
  def _replace(self, /, **kwds):
168
206
  def generator():
@@ -174,29 +212,57 @@ def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=
174
212
 
175
213
  result = self._make(iter(generator()))
176
214
  if kwds:
177
- raise ValueError(f'Got unexpected field names: {list(kwds)!r}')
215
+ raise ValueError(f"Got unexpected field names: {list(kwds)!r}")
178
216
  return result
179
217
 
180
- _replace.__doc__ = (f'Return a new {typename} object replacing specified '
181
- 'fields with new values')
218
+ _replace.__doc__ = (
219
+ f"Return a new {typename} object replacing specified " "fields with new values"
220
+ )
182
221
 
183
222
  def __repr__(self):
184
- 'Return a nicely formatted representation string'
223
+ "Return a nicely formatted representation string"
185
224
  return self.__class__.__name__ + repr_fmt % self
186
225
 
187
226
  def _asdict(self):
188
- 'Return a new dict which maps field names to their values.'
227
+ "Return a new dict which maps field names to their values."
189
228
  return _dict(_zip(arrange_names, self))
190
229
 
191
230
  def __getnewargs__(self):
192
- 'Return self as a plain tuple. Used by copy and pickle.'
231
+ "Return self as a plain tuple. Used by copy and pickle."
193
232
  return _tuple(self)
194
233
 
195
- @swizzle_attributes_retriever(sep=sep, type = swizzledtuple)
234
+ @swizzle_attributes_retriever(sep=sep, type=swizzledtuple, only_attrs=field_names)
196
235
  def __getattribute__(self, attr_name):
197
236
  return super(_tuple, self).__getattribute__(attr_name)
198
237
 
238
+ def __getitem__(self, index):
239
+ if not isinstance(index, slice):
240
+ return _tuple.__getitem__(self, index)
199
241
 
242
+ selected_indices = arrange_indices[index]
243
+ selected_values = _tuple.__getitem__(self, index)
244
+
245
+ seen = set()
246
+ filtered = [
247
+ (i, v, field_names[i])
248
+ for i, v in zip(selected_indices, selected_values)
249
+ if not (i in seen or seen.add(i))
250
+ ]
251
+
252
+ if filtered:
253
+ _, filtered_values, filtered_names = zip(*filtered)
254
+ else:
255
+ filtered_values, filtered_names = (), ()
256
+
257
+ return swizzledtuple(
258
+ typename,
259
+ filtered_names,
260
+ rename=rename,
261
+ defaults=filtered_values,
262
+ module=module,
263
+ arrange_names=arrange_names[index],
264
+ sep=sep,
265
+ )()
200
266
 
201
267
  for method in (
202
268
  __new__,
@@ -206,27 +272,29 @@ def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=
206
272
  _asdict,
207
273
  __getnewargs__,
208
274
  __getattribute__,
275
+ __getitem__,
209
276
  ):
210
- method.__qualname__ = f'{typename}.{method.__name__}'
277
+ method.__qualname__ = f"{typename}.{method.__name__}"
211
278
 
212
279
  class_namespace = {
213
- '__doc__': f'{typename}({arg_list})',
214
- '__slots__': (),
215
- '_fields': field_names,
216
- '_field_defaults': field_defaults,
217
- '__new__': __new__,
218
- '_make': _make,
219
- '_replace': _replace,
220
- '__repr__': __repr__,
221
- '_asdict': _asdict,
222
- '__getnewargs__': __getnewargs__,
223
- '__getattribute__': __getattribute__
280
+ "__doc__": f"{typename}({arg_list})",
281
+ "__slots__": (),
282
+ "_fields": field_names,
283
+ "_field_defaults": field_defaults,
284
+ "__new__": __new__,
285
+ "_make": _make,
286
+ "_replace": _replace,
287
+ "__repr__": __repr__,
288
+ "_asdict": _asdict,
289
+ "__getnewargs__": __getnewargs__,
290
+ "__getattribute__": __getattribute__,
291
+ "__getitem__": __getitem__,
224
292
  }
225
293
  seen = set()
226
294
  for index, name in enumerate(arrange_names):
227
295
  if name in seen:
228
296
  continue
229
- doc = _sys.intern(f'Alias for field number {index}')
297
+ doc = _sys.intern(f"Alias for field number {index}")
230
298
  class_namespace[name] = _tuplegetter(index, doc)
231
299
  seen.add(name)
232
300
 
@@ -234,10 +302,10 @@ def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=
234
302
 
235
303
  if module is None:
236
304
  try:
237
- module = _sys._getframemodulename(1) or '__main__'
305
+ module = _sys._getframemodulename(1) or "__main__"
238
306
  except AttributeError:
239
307
  try:
240
- module = _sys._getframe(1).f_globals.get('__name__', '__main__')
308
+ module = _sys._getframe(1).f_globals.get("__name__", "__main__")
241
309
  except (AttributeError, ValueError):
242
310
  pass
243
311
  if module is not None:
@@ -245,27 +313,69 @@ def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=
245
313
 
246
314
  return result
247
315
 
316
+
248
317
  # Helper function to split a string based on a sep
249
318
  def split_string(string, sep):
250
- if sep == '':
251
- return list(string)
319
+ if sep[0] == "+":
320
+ n = int(sep)
321
+ return [string[i : i + n] for i in range(0, len(string), n)]
252
322
  else:
253
323
  return string.split(sep)
254
324
 
325
+
255
326
  # Helper function to collect attribute retrieval functions from a class or meta-class
256
327
  def collect_attribute_functions(cls):
257
328
  funcs = []
258
- if hasattr(cls, '__getattribute__'):
329
+ if hasattr(cls, "__getattribute__"):
259
330
  funcs.append(cls.__getattribute__)
260
- if hasattr(cls, '__getattr__'):
331
+ if hasattr(cls, "__getattr__"):
261
332
  funcs.append(cls.__getattr__)
262
333
  if not funcs:
263
- raise AttributeError("No __getattr__ or __getattribute__ found on the class or meta-class")
334
+ raise AttributeError(
335
+ "No __getattr__ or __getattribute__ found on the class or meta-class"
336
+ )
264
337
  return funcs
265
338
 
339
+
266
340
  # Function to combine multiple attribute retrieval functions
267
341
 
268
- def swizzle_attributes_retriever(attribute_funcs=None, sep=None, type = swizzledtuple):
342
+
343
+ def is_valid_sep(s):
344
+ # if not s:
345
+ # return False
346
+ if s[0] == "+" and s[1:].isdigit():
347
+ return True
348
+ for ch in s:
349
+ if ch == "_":
350
+ continue
351
+ cat = unicodedata.category(ch)
352
+ if not (cat.startswith("L") or cat == "Nd"):
353
+ return False
354
+ return True
355
+
356
+
357
+ def swizzle_attributes_retriever(
358
+ attribute_funcs=None, sep=None, type=swizzledtuple, only_attrs=None
359
+ ):
360
+ trie = None
361
+
362
+ if sep == "":
363
+ sep = "+1" # for backwards compatibility, remove on next version
364
+
365
+ if sep is None and only_attrs:
366
+ only_attrs_length = set(len(fname) for fname in only_attrs)
367
+ if len(only_attrs_length) == 1:
368
+ sep = f"+{next(iter(only_attrs_length))}"
369
+ else:
370
+ trie = Trie(only_attrs)
371
+
372
+ if sep is not None and not is_valid_sep(sep):
373
+ raise ValueError(
374
+ f"Invalid value for sep: {sep!r}. Must be either:"
375
+ " (1) a non-empty string containing only letters, digits, or underscores, "
376
+ "or (2) a pattern of the form '+N' where N is a positive integer."
377
+ )
378
+
269
379
  def _swizzle_attributes_retriever(attribute_funcs):
270
380
  if not isinstance(attribute_funcs, list):
271
381
  attribute_funcs = [attribute_funcs]
@@ -292,11 +402,25 @@ def swizzle_attributes_retriever(attribute_funcs=None, sep=None, type = swizzled
292
402
  attr_parts = split_string(attr_name, sep)
293
403
  arranged_names = attr_parts
294
404
  for part in attr_parts:
405
+ if only_attrs and part not in only_attrs:
406
+ raise AttributeError(
407
+ f"Attribute {part} is not part of an allowed field for swizzling"
408
+ )
295
409
  attribute = retrieve_attribute(obj, part)
296
410
  if attribute is not MISSING:
297
411
  matched_attributes.append(attribute)
298
412
  else:
299
- raise AttributeError(f"No matching attribute found for part: {part}")
413
+ raise AttributeError(f"No matching attribute found for {part}")
414
+ elif only_attrs:
415
+ arranged_names = trie.split_longest_prefix(attr_name)
416
+ if arranged_names is None:
417
+ raise AttributeError(f"No matching attribute found for {attr_name}")
418
+ for name in arranged_names:
419
+ attribute = retrieve_attribute(obj, name)
420
+ if attribute is not MISSING:
421
+ matched_attributes.append(attribute)
422
+ else:
423
+ raise AttributeError(f"No matching attribute found for {name}")
300
424
  else:
301
425
  # No sep provided, attempt to match substrings
302
426
  i = 0
@@ -312,7 +436,9 @@ def swizzle_attributes_retriever(attribute_funcs=None, sep=None, type = swizzled
312
436
  match_found = True
313
437
  break
314
438
  if not match_found:
315
- raise AttributeError(f"No matching attribute found for substring: {attr_name[i:]}")
439
+ raise AttributeError(
440
+ f"No matching attribute found for substring: {attr_name[i:]}"
441
+ )
316
442
 
317
443
  if type == swizzledtuple:
318
444
  field_names = set(arranged_names)
@@ -323,11 +449,15 @@ def swizzle_attributes_retriever(attribute_funcs=None, sep=None, type = swizzled
323
449
  elif hasattr(obj, "__class__"):
324
450
  if hasattr(obj.__class__, "__name__"):
325
451
  name = obj.__class__.__name__
326
- result = type(name, field_names, arrange_names=arranged_names)
452
+ result = type(
453
+ name,
454
+ field_names,
455
+ arrange_names=arranged_names,
456
+ sep=sep,
457
+ )
327
458
  result = result(*field_values)
328
459
  return result
329
460
 
330
-
331
461
  return type(matched_attributes)
332
462
 
333
463
  return retrieve_swizzled_attributes
@@ -337,28 +467,66 @@ def swizzle_attributes_retriever(attribute_funcs=None, sep=None, type = swizzled
337
467
  else:
338
468
  return _swizzle_attributes_retriever
339
469
 
340
- # Decorator function to enable swizzling for a class
341
- def swizzle(cls=None, meta=False, sep=None, type = tuple):
470
+
471
+ def swizzle(cls=None, meta=False, sep=None, type=tuple, only_attrs=None):
472
+
473
+ def preserve_metadata(
474
+ target,
475
+ source,
476
+ keys=("__name__", "__qualname__", "__doc__", "__module__", "__annotations__"),
477
+ ):
478
+ for key in keys:
479
+ if hasattr(source, key):
480
+ try:
481
+ setattr(target, key, getattr(source, key))
482
+ except (TypeError, AttributeError):
483
+ pass # some attributes may be read-only
342
484
 
343
485
  def class_decorator(cls):
344
486
  # Collect attribute retrieval functions from the class
345
487
  attribute_funcs = collect_attribute_functions(cls)
346
488
 
347
489
  # Apply the swizzling to the class's attribute retrieval
348
- setattr(cls, attribute_funcs[-1].__name__, swizzle_attributes_retriever(attribute_funcs, sep, type))
490
+ setattr(
491
+ cls,
492
+ attribute_funcs[-1].__name__,
493
+ swizzle_attributes_retriever(attribute_funcs, sep, type, only_attrs),
494
+ )
349
495
 
350
496
  # Handle meta-class swizzling if requested
351
497
  if meta:
352
498
  meta_cls = _type(cls)
353
- if meta_cls == _type:
354
- class SwizzledMetaType(meta_cls):
499
+
500
+ class SwizzledMetaType(meta_cls):
501
+ pass
502
+
503
+ if meta_cls == EnumType:
504
+
505
+ def cfem_dummy(*args, **kwargs):
355
506
  pass
356
- meta_cls = SwizzledMetaType
357
- cls = meta_cls(cls.__name__, cls.__bases__, dict(cls.__dict__))
358
507
 
359
- meta_funcs = collect_attribute_functions(meta_cls)
360
- setattr(meta_cls, meta_funcs[-1].__name__, swizzle_attributes_retriever(meta_funcs, sep, type))
508
+ cfem = SwizzledMetaType._check_for_existing_members_
509
+ SwizzledMetaType._check_for_existing_members_ = cfem_dummy
510
+
511
+ class SwizzledClass(cls, metaclass=SwizzledMetaType):
512
+ pass
513
+
514
+ if meta_cls == EnumType:
515
+ SwizzledMetaType._check_for_existing_members_ = cfem
516
+
517
+ # Preserve metadata on swizzled meta and class
518
+ preserve_metadata(SwizzledMetaType, meta_cls)
519
+ preserve_metadata(SwizzledClass, cls)
361
520
 
521
+ meta_cls = SwizzledMetaType
522
+ cls = SwizzledClass
523
+
524
+ meta_funcs = collect_attribute_functions(meta_cls)
525
+ setattr(
526
+ meta_cls,
527
+ meta_funcs[-1].__name__,
528
+ swizzle_attributes_retriever(meta_funcs, sep, type, only_attrs),
529
+ )
362
530
  return cls
363
531
 
364
532
  if cls is None:
@@ -372,7 +540,10 @@ class Swizzle(types.ModuleType):
372
540
  types.ModuleType.__init__(self, __name__)
373
541
  self.__dict__.update(_sys.modules[__name__].__dict__)
374
542
 
375
- def __call__(self, cls=None, meta=False, sep = None, type = swizzledtuple):
376
- return swizzle(cls, meta, sep, type)
543
+ def __call__(
544
+ self, cls=None, meta=False, sep=None, type=swizzledtuple, only_attrs=None
545
+ ):
546
+ return swizzle(cls, meta, sep, type, only_attrs)
547
+
377
548
 
378
549
  _sys.modules[__name__] = Swizzle()
swizzle/trie.py ADDED
@@ -0,0 +1,61 @@
1
+ class TrieNode:
2
+ def __init__(self):
3
+ self.children = {}
4
+ self.is_end = False
5
+
6
+ def add(self, word):
7
+ node = self
8
+ for char in word:
9
+ if char not in node.children:
10
+ node.children[char] = TrieNode()
11
+ node = node.children[char]
12
+ node.is_end = True
13
+
14
+ def step(self, char):
15
+ return self.children.get(char)
16
+
17
+
18
+ class Trie:
19
+ def __init__(self, words=None):
20
+ self.root = TrieNode()
21
+ if words:
22
+ for word in words:
23
+ self.add(word)
24
+
25
+ def add(self, word):
26
+ self.root.add(word)
27
+
28
+ def split_longest_prefix(self, query):
29
+ result = []
30
+ length = len(query)
31
+ i = 0
32
+ while i < length:
33
+ node = self.root
34
+ longest_end = -1
35
+ j = i
36
+ children = node.children # cache children dict
37
+ while j < length:
38
+ next_node = children.get(query[j])
39
+ if not next_node:
40
+ break
41
+ node = next_node
42
+ children = node.children # update cache
43
+ j += 1
44
+ if node.is_end:
45
+ longest_end = j
46
+ if longest_end == -1:
47
+ return None
48
+ result.append(query[i:longest_end])
49
+ i = longest_end
50
+ return result
51
+
52
+
53
+ if __name__ == "__main__":
54
+ words = ["mango", "man", "go", "bat", "manbat"]
55
+ trie = Trie(words)
56
+
57
+ print(trie.split_longest_prefix("mangomanmanbat")) # ['mango', 'man', 'bat']
58
+ print(trie.split_longest_prefix("manbat")) # ['manbat']
59
+ print(trie.split_longest_prefix("mango")) # ['mango']
60
+ print(trie.split_longest_prefix("mangoman")) # ['mango', 'man']
61
+ print(trie.split_longest_prefix("unknownword")) # None
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: swizzle
3
- Version: 2.2.1
3
+ Version: 2.3.1
4
4
  Summary: Swizzle enables the retrieval of multiple attributes, similar to swizzling in computer graphics.
5
5
  Home-page: https://github.com/janthmueller/swizzle
6
6
  Author: Jan T. Müller
@@ -17,6 +17,17 @@ Classifier: Programming Language :: Python :: 3 :: Only
17
17
  Requires-Python: >=3.6
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: license
27
+ Dynamic: license-file
28
+ Dynamic: project-url
29
+ Dynamic: requires-python
30
+ Dynamic: summary
20
31
 
21
32
  # Swizzle
22
33
 
@@ -0,0 +1,7 @@
1
+ swizzle/__init__.py,sha256=8lOReILgZiQ38ez_BTRsVTbWbHJ2cHfQM9edkU6voOQ,19996
2
+ swizzle/trie.py,sha256=JutRoUHut2kbNJZ8hmHMCaXP8QHGl7KXzLv2rRVFrb0,1791
3
+ swizzle-2.3.1.dist-info/licenses/LICENSE,sha256=WDAegKWtl3rZUiN-SQ2FEQQwEFxlM_jEKQyJRJawJXo,1070
4
+ swizzle-2.3.1.dist-info/METADATA,sha256=QYsGAWzGAB3i25i6zZZOqHM7PkIbMmEqNleLNOr-ttk,5684
5
+ swizzle-2.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
+ swizzle-2.3.1.dist-info/top_level.txt,sha256=XFSQti81x2zM0zAMCY1YD0lqB1eSg5my9BB03uFgCic,8
7
+ swizzle-2.3.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.3.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,6 +0,0 @@
1
- swizzle/__init__.py,sha256=_clk7y4NCO9KY-I-iSNsLc6iT8ylLcNPD4aiqoun1NE,15304
2
- swizzle-2.2.1.dist-info/LICENSE,sha256=WDAegKWtl3rZUiN-SQ2FEQQwEFxlM_jEKQyJRJawJXo,1070
3
- swizzle-2.2.1.dist-info/METADATA,sha256=vsupVSVUEg4uC3ZodBYPaLyUhvVPSXr2AgG9om0WMlY,5450
4
- swizzle-2.2.1.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
5
- swizzle-2.2.1.dist-info/top_level.txt,sha256=XFSQti81x2zM0zAMCY1YD0lqB1eSg5my9BB03uFgCic,8
6
- swizzle-2.2.1.dist-info/RECORD,,