swizzle 2.3.0__tar.gz → 2.3.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swizzle
3
- Version: 2.3.0
3
+ Version: 2.3.2
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
@@ -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.3.0"
21
+ __version__ = "2.3.2"
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 None.
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,33 +212,29 @@ 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
 
199
- # def __getitem__(self, index):
200
- # a_names = arrange_names[index]
201
- # _sep = '' if sep is None else sep
202
- # return getattr(self, _sep.join(a_names))
203
-
204
238
  def __getitem__(self, index):
205
239
  if not isinstance(index, slice):
206
240
  return _tuple.__getitem__(self, index)
@@ -227,11 +261,9 @@ def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=
227
261
  defaults=filtered_values,
228
262
  module=module,
229
263
  arrange_names=arrange_names[index],
230
- sep=sep
264
+ sep=sep,
231
265
  )()
232
266
 
233
-
234
-
235
267
  for method in (
236
268
  __new__,
237
269
  _make.__func__,
@@ -240,29 +272,29 @@ def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=
240
272
  _asdict,
241
273
  __getnewargs__,
242
274
  __getattribute__,
243
- __getitem__
275
+ __getitem__,
244
276
  ):
245
- method.__qualname__ = f'{typename}.{method.__name__}'
277
+ method.__qualname__ = f"{typename}.{method.__name__}"
246
278
 
247
279
  class_namespace = {
248
- '__doc__': f'{typename}({arg_list})',
249
- '__slots__': (),
250
- '_fields': field_names,
251
- '_field_defaults': field_defaults,
252
- '__new__': __new__,
253
- '_make': _make,
254
- '_replace': _replace,
255
- '__repr__': __repr__,
256
- '_asdict': _asdict,
257
- '__getnewargs__': __getnewargs__,
258
- '__getattribute__': __getattribute__,
259
- '__getitem__': __getitem__
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__,
260
292
  }
261
293
  seen = set()
262
294
  for index, name in enumerate(arrange_names):
263
295
  if name in seen:
264
296
  continue
265
- doc = _sys.intern(f'Alias for field number {index}')
297
+ doc = _sys.intern(f"Alias for field number {index}")
266
298
  class_namespace[name] = _tuplegetter(index, doc)
267
299
  seen.add(name)
268
300
 
@@ -270,10 +302,10 @@ def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=
270
302
 
271
303
  if module is None:
272
304
  try:
273
- module = _sys._getframemodulename(1) or '__main__'
305
+ module = _sys._getframemodulename(1) or "__main__"
274
306
  except AttributeError:
275
307
  try:
276
- module = _sys._getframe(1).f_globals.get('__name__', '__main__')
308
+ module = _sys._getframe(1).f_globals.get("__name__", "__main__")
277
309
  except (AttributeError, ValueError):
278
310
  pass
279
311
  if module is not None:
@@ -281,27 +313,69 @@ def swizzledtuple(typename, field_names, *, rename=False, defaults=None, module=
281
313
 
282
314
  return result
283
315
 
316
+
284
317
  # Helper function to split a string based on a sep
285
318
  def split_string(string, sep):
286
- if sep == '':
287
- 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)]
288
322
  else:
289
323
  return string.split(sep)
290
324
 
325
+
291
326
  # Helper function to collect attribute retrieval functions from a class or meta-class
292
327
  def collect_attribute_functions(cls):
293
328
  funcs = []
294
- if hasattr(cls, '__getattribute__'):
329
+ if hasattr(cls, "__getattribute__"):
295
330
  funcs.append(cls.__getattribute__)
296
- if hasattr(cls, '__getattr__'):
331
+ if hasattr(cls, "__getattr__"):
297
332
  funcs.append(cls.__getattr__)
298
333
  if not funcs:
299
- 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
+ )
300
337
  return funcs
301
338
 
339
+
302
340
  # Function to combine multiple attribute retrieval functions
303
341
 
304
- 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
+
305
379
  def _swizzle_attributes_retriever(attribute_funcs):
306
380
  if not isinstance(attribute_funcs, list):
307
381
  attribute_funcs = [attribute_funcs]
@@ -328,11 +402,25 @@ def swizzle_attributes_retriever(attribute_funcs=None, sep=None, type = swizzled
328
402
  attr_parts = split_string(attr_name, sep)
329
403
  arranged_names = attr_parts
330
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
+ )
331
409
  attribute = retrieve_attribute(obj, part)
332
410
  if attribute is not MISSING:
333
411
  matched_attributes.append(attribute)
334
412
  else:
335
- 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}")
336
424
  else:
337
425
  # No sep provided, attempt to match substrings
338
426
  i = 0
@@ -348,7 +436,9 @@ def swizzle_attributes_retriever(attribute_funcs=None, sep=None, type = swizzled
348
436
  match_found = True
349
437
  break
350
438
  if not match_found:
351
- 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
+ )
352
442
 
353
443
  if type == swizzledtuple:
354
444
  field_names = set(arranged_names)
@@ -359,11 +449,15 @@ def swizzle_attributes_retriever(attribute_funcs=None, sep=None, type = swizzled
359
449
  elif hasattr(obj, "__class__"):
360
450
  if hasattr(obj.__class__, "__name__"):
361
451
  name = obj.__class__.__name__
362
- 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
+ )
363
458
  result = result(*field_values)
364
459
  return result
365
460
 
366
-
367
461
  return type(matched_attributes)
368
462
 
369
463
  return retrieve_swizzled_attributes
@@ -373,28 +467,66 @@ def swizzle_attributes_retriever(attribute_funcs=None, sep=None, type = swizzled
373
467
  else:
374
468
  return _swizzle_attributes_retriever
375
469
 
376
- # Decorator function to enable swizzling for a class
377
- 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
378
484
 
379
485
  def class_decorator(cls):
380
486
  # Collect attribute retrieval functions from the class
381
487
  attribute_funcs = collect_attribute_functions(cls)
382
488
 
383
489
  # Apply the swizzling to the class's attribute retrieval
384
- 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
+ )
385
495
 
386
496
  # Handle meta-class swizzling if requested
387
497
  if meta:
388
498
  meta_cls = _type(cls)
389
- if meta_cls == _type:
390
- 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):
391
506
  pass
392
- meta_cls = SwizzledMetaType
393
- cls = meta_cls(cls.__name__, cls.__bases__, dict(cls.__dict__))
394
507
 
395
- meta_funcs = collect_attribute_functions(meta_cls)
396
- 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)
397
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
+ )
398
530
  return cls
399
531
 
400
532
  if cls is None:
@@ -403,12 +535,19 @@ def swizzle(cls=None, meta=False, sep=None, type = tuple):
403
535
  return class_decorator(cls)
404
536
 
405
537
 
538
+ t = swizzledtuple
539
+ # c = swizzledclass
540
+
541
+
406
542
  class Swizzle(types.ModuleType):
407
543
  def __init__(self):
408
544
  types.ModuleType.__init__(self, __name__)
409
545
  self.__dict__.update(_sys.modules[__name__].__dict__)
410
546
 
411
- def __call__(self, cls=None, meta=False, sep = None, type = swizzledtuple):
412
- return swizzle(cls, meta, sep, type)
547
+ def __call__(
548
+ self, cls=None, meta=False, sep=None, type=swizzledtuple, only_attrs=None
549
+ ):
550
+ return swizzle(cls, meta, sep, type, only_attrs)
551
+
413
552
 
414
553
  _sys.modules[__name__] = Swizzle()
@@ -0,0 +1,50 @@
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: swizzle
3
- Version: 2.3.0
3
+ Version: 2.3.2
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
@@ -2,6 +2,7 @@ LICENSE
2
2
  README.md
3
3
  setup.py
4
4
  swizzle/__init__.py
5
+ swizzle/trie.py
5
6
  swizzle.egg-info/PKG-INFO
6
7
  swizzle.egg-info/SOURCES.txt
7
8
  swizzle.egg-info/dependency_links.txt
File without changes
File without changes
File without changes
File without changes