fluently 0.9.0__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.
@@ -0,0 +1,670 @@
1
+ Metadata-Version: 2.4
2
+ Name: fluently
3
+ Version: 0.9.0
4
+ Summary: Fluent data type container implementations for Python.
5
+ Author: Daniel Sissman
6
+ License-Expression: MIT
7
+ Project-URL: documentation, https://github.com/bluebinary/fluently/blob/main/README.md
8
+ Project-URL: changelog, https://github.com/bluebinary/fluently/blob/main/CHANGELOG.md
9
+ Project-URL: repository, https://github.com/bluebinary/fluently
10
+ Project-URL: issues, https://github.com/bluebinary/fluently/issues
11
+ Project-URL: homepage, https://github.com/bluebinary/fluently
12
+ Keywords: fluent,containers,data types
13
+ Platform: any
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ Provides-Extra: development
23
+ Requires-Dist: black==24.10.*; extra == "development"
24
+ Requires-Dist: pytest==8.3.*; extra == "development"
25
+ Requires-Dist: pytest-codeblocks==0.17.0; extra == "development"
26
+ Requires-Dist: pyflakes==3.4.0; extra == "development"
27
+ Provides-Extra: distribution
28
+ Requires-Dist: build; extra == "distribution"
29
+ Requires-Dist: twine; extra == "distribution"
30
+ Requires-Dist: wheel; extra == "distribution"
31
+
32
+ # Fluently
33
+
34
+ The `fluently` library provides several container data type implementations with fluent interfaces:
35
+
36
+ * a `list` subclass with a fluent interface
37
+ * a `set` subclass with a fluent interface
38
+ * a `tuple` subclass with a fluent interface
39
+
40
+ ### Requirements
41
+
42
+ The Fluently library has been tested with Python 3.10, 3.11, 3.12, 3.13 and 3.14. The
43
+ library is not compatible with Python 3.9 or earlier.
44
+
45
+ ### Installation
46
+
47
+ The Fluently library is available from PyPI, so may be added to a project's dependencies
48
+ via its `requirements.txt` file or similar by referencing the Fluently library's name,
49
+ `fluently`, or the library may be installed directly into your local runtime environment
50
+ using `pip` via the `pip install` command by entering the following into your shell:
51
+
52
+ $ pip install fluently
53
+
54
+ ### Example Usage
55
+
56
+ To use the Fluently library, import the library and the data type or data types you
57
+ need and use them just like their regular counterparts, with the knowledge that they
58
+ support setting and getting values without regard to the casing of any string keys:
59
+
60
+ #### Fluent List
61
+
62
+ ```python
63
+ # The 'flulist' and 'flist' aliases can be used interchangeably with 'fluentlist'
64
+ from fluently import fluentlist, flulist, flist
65
+
66
+ # Create a new fluentlist instance
67
+ data = fluentlist(["A", "B", "C"])
68
+
69
+ # Assert that the list has the expected class identity, aliases and superclass
70
+ assert isinstance(data, fluentlist)
71
+ assert isinstance(data, flulist)
72
+ assert isinstance(data, flist)
73
+ assert isinstance(data, list)
74
+
75
+ # Assert that the list has the expected length
76
+ assert data.length() == len(data) == 3
77
+
78
+ # Assert that the list has the expected contents
79
+ assert "A" in data
80
+ assert "B" in data
81
+ assert "C" in data
82
+
83
+ # Append item in-place into original list, then create a new list of unique values
84
+ uniquedata = data.append("C").unique()
85
+
86
+ # Assert that the original list now contains the appended value
87
+ assert len(data) == 4
88
+ assert data == ["A", "B", "C", "C"]
89
+
90
+ # Assert that the unique list contains only the unique values
91
+ assert len(uniquedata) == 3
92
+ assert uniquedata == ["A", "B", "C"]
93
+ ```
94
+
95
+ #### Fluent Set
96
+
97
+ ```python
98
+ # The 'fluset' and 'fset' aliases can be used interchangeably with 'fluentset'
99
+ from fluently import fluentset, fluset, fset
100
+
101
+ # Create a new fluentset instance
102
+ data = fluentset(["A", "B", "C"])
103
+
104
+ # Assert that the set has the expected class identity, aliases and superclass
105
+ assert isinstance(data, fluentset)
106
+ assert isinstance(data, fluset)
107
+ assert isinstance(data, fset)
108
+ assert isinstance(data, set)
109
+
110
+ # Assert that the set has the expected length
111
+ assert data.length() == len(data) == 3
112
+
113
+ # Assert that the set has the expected contents
114
+ assert "A" in data
115
+ assert "B" in data
116
+ assert "C" in data
117
+ ```
118
+
119
+ #### Fluent Tuple
120
+
121
+ ```python
122
+ # The 'flutuple' and 'ftuple' aliases can be used interchangeably with 'fluenttuple'
123
+ from fluently import fluenttuple, flutuple, ftuple
124
+
125
+ # Create a new fluenttuple instance
126
+ data = fluenttuple(["A", "B", "C"])
127
+
128
+ # Assert that the set has the expected class identity, aliases and superclass
129
+ assert isinstance(data, fluenttuple)
130
+ assert isinstance(data, flutuple)
131
+ assert isinstance(data, ftuple)
132
+ assert isinstance(data, tuple)
133
+
134
+ # Assert that the set has the expected length
135
+ assert data.length() == len(data) == 3
136
+
137
+ # Assert that the set has the expected contents
138
+ assert "A" in data
139
+ assert "B" in data
140
+ assert "C" in data
141
+ ```
142
+
143
+ ### Classes & Methods
144
+
145
+ The Fluently library provides the following data type subclasses and each class offers
146
+ shorthand aliases that prefixes the superclass' name with `flu` or `f` to denote that
147
+ the class is its fluent variant. The aliases can be used interchangeably with the fully
148
+ qualified subclass names as they are direct aliases rather than further subclasses.
149
+
150
+ | Subclass | Superclass | Subclass Alias | Short Subclass Alias |
151
+ |-----------------|------------|----------------|----------------------|
152
+ | `fluentlist` | `list` | `flulist` | `flist` |
153
+ | `fluentset` | `set` | `fluset` | `fset` |
154
+ | `fluenttuple` | `tuple` | `flutuple` | `ftuple` |
155
+
156
+ The Fluently library classes can be used interchangeably with their superclasses where
157
+ one wishes to use a fluent chainable interface to interact with the container types.
158
+ The classes offer all the usual functionality that their superclasses offer, and all the
159
+ usual methods and interactions are available, in addition to the library provided fluent
160
+ methods as documented below. All methods marked with the link symbol, 🔗, can be chained.
161
+
162
+ #### Fluent List Methods
163
+
164
+ The `fluentlist` class provides the following methods in addition to the methods provided
165
+ by the `list` superclass:
166
+
167
+ * `length()` (`int`) – The `length()` method supports returning the total count of items
168
+ in the current list. The method returns the count as an `int` value, so does not allow
169
+ further chaining, but can be used as the last call on chain of other `fluentlist` methods
170
+ that do support chaining.
171
+
172
+ * `clone()` (`fluentlist`) – The `clone()` method supports creating a cloned copy of the
173
+ current list, that contains the same items, in a separate `fluentlist` instance.
174
+
175
+ * `prepend(item: object)` 🔗 (`fluentlist`) – The `prepend()` method supports prepending
176
+ the specified item to the start of the current list.
177
+
178
+ * `append(item: object)` 🔗 (`fluentlist`) – The `append()` method supports appending the
179
+ specified item to the end of the current list.
180
+
181
+ * `extend(iterable: object)` 🔗 (`fluentlist`) – The `extend()` method supports appending
182
+ the specified items from the specified `iterable` object to the end of the current list.
183
+
184
+ * `insert(index: int, item: object)` 🔗 (`fluentlist`) – The `insert()` method supports
185
+ appending the specified item into the current list at the specified `index` position.
186
+ The `index` must be in range of the current length of the list.
187
+
188
+ * `remove(item: object, raises: bool = True)` 🔗 (`fluentlist`) – The `remove()` method
189
+ supports removing the first occurrence of the specified item from the current list. If
190
+ the specified `item` does not exist in the list, and if the `raises` keyword argument
191
+ is set to its default value of `True`, a `ValueError` exception will be raised noting
192
+ the absence of the `item` value in the list. If `raises` is set to `False` no exception
193
+ will be raised, instead the error will be logged, visible via the standard `logging`
194
+ library if the log level is set to `ERROR` or higher.
195
+
196
+ * `removeall(item: object, raises: bool = True)` 🔗 (`fluentlist`) – The `removeall()`
197
+ method supports removing all occurrences of the specified item from the current list.
198
+ If the specified `item` does not exist in the list, and if the `raises` keyword argument
199
+ is set to its default value of `True`, a `ValueError` exception will be raised noting
200
+ the absence of the `item` value in the list. If `raises` is set to `False` no exception
201
+ will be raised, instead the error will be logged, visible via the standard `logging`
202
+ library if the log level is set to `ERROR` or higher.
203
+
204
+ * `discard(item: object)` 🔗 (`fluentlist`) – The `discard()` method supports removing
205
+ the first occurrence of the specified `item` from the current list if the item is present
206
+ in the list. No error is raised if the `item` does not exist in the list. Added for
207
+ consistency with sets.
208
+
209
+ * `clear()` 🔗 (`fluentlist`) – The `clear()` method supports clearing all of the items
210
+ from the current list.
211
+
212
+ * `repeat(count: int)` 🔗 (`fluentlist`) – The `repeat()` method supports repeating all
213
+ the items from the current list by appending the items in the list to the end of the list
214
+ in the order in which they were originally added to the list. The items will be repeated
215
+ according to the specified `count` value. The method extends the current list with the
216
+ repeated items rather than returning the repeated sequence as a new list.
217
+
218
+ * `reverse()` 🔗 (`fluentlist`) – The `reverse()` method supports reversing the order
219
+ of the items from the current list. The list is reversed in-place.
220
+
221
+ * `shuffle()` 🔗 (`fluentlist`) – The `shuffle()` method supports randomly shuffling all
222
+ of the items from the current list. The list is shuffled in-place.
223
+
224
+ * `slice(start: int, stop: int = None, step: int = 1)` 🔗 (`fluentlist`) – The `slice()`
225
+ method supports returning a slice of the items from the current list according to the
226
+ specified slice indices and step count. The method returns the slice within a new list;
227
+ it does not modify the original list.
228
+
229
+ * `take(index: int)` 🔗 (`fluentlist`) – The `take()` method supports returning a slice
230
+ of the items from the current list from the beginning of the list at index 0 to the stop
231
+ index as specified by the provided `index` argument. The method returns the slice within
232
+ a new list; it does not modify the original list.
233
+
234
+ * `drop(index: int)` 🔗 (`fluentlist`) – The `drop()` method supports returning a slice
235
+ of the items from the current list from the start index as specified by the provided
236
+ `index` argument until the end of the list. The method returns the slice within a new
237
+ list; it does not modify the original list.
238
+
239
+ * `swap(source: int, target: int)` 🔗 (`fluentlist`) – The `swap()` method supports
240
+ swapping the items in the list at the specified `source` and `target` indices with each
241
+ other. The items are swapped in-place, modifying the current list.
242
+
243
+ * `unique()` 🔗 (`fluentlist`) – The `unique()` method supports providing a list of the
244
+ unique items from the current list. The method returns a new list containing the unique
245
+ items found in the current list; the original current list is not modified.
246
+
247
+ * `count(item: object)` (`int`) – The `count()` method supports providing a count of the
248
+ number of times the specified `item` value appears in the current list. The method returns
249
+ the count as an `int` value, so does not allow further chaining, but can be used as the
250
+ last call on chain of other `fluentlist` methods that do support chaining.
251
+
252
+ * `contains(item: object)` (`bool`) – The `contains()` method supports returning whether
253
+ the specified `item` value appears in the current list at least once or not. The method
254
+ returns a `bool` value indicating the presence or absence of the specified `item` value,
255
+ so does not allow further chaining, but can be used as the last call on chain of other
256
+ `fluentlist` methods that do support chaining.
257
+
258
+ * `any(item: object)` (`bool`) – The `any()` method supports returning whether the current
259
+ list contains the specified `item` value at least once or not. As the method returns a
260
+ `bool` value, it does not allow further chaining, but can be used as the last call on
261
+ chain of other `fluentlist` methods that do support chaining.
262
+
263
+ * `all(item: object)` (`bool`) – The `all()` method supports returning whether the current
264
+ list solely contains the specified `item` value or not; that is for the `all()` method
265
+ to return `True`, the current list must only contain items that match the specified `item`
266
+ via the equality comparison `==` operator. As the method returns a `bool` value, it does
267
+ not allow further chaining, but can be used as the last call on chain of other `fluentlist`
268
+ methods that do support chaining.
269
+
270
+ * `map(function: callable)` 🔗 (`fluentlist`) – The `map()` method supports running the
271
+ specified `function` on each item in the current list. The method returns a new list
272
+ containing the result of running the `function` on each item in the current list rather
273
+ than modifying the current list in-place.
274
+
275
+ * `reduce(function: callable)` (`object`) – The `reduce()` method supports running the
276
+ specified `function` on each item in the current list, reducing the list down to a single
277
+ value, which the method returns upon completion. The method therefore does not support
278
+ chaining but can be as the last call on a chain of other `fluentlist` methods that do
279
+ support chaining.
280
+
281
+ * `sort(key: object = None, reversed: bool = False)` 🔗 (`fluentlist`) – The `sort()` method
282
+ supports sorting the current list in-place, according to any specified `key` and `reversed`
283
+ arguments. The contents of the current list will be updated to reflect the specified sort.
284
+
285
+ * `sorted(key: object = None, reversed: bool = False)` 🔗 (`fluentlist`) – The `sorted()`
286
+ method supports sorting the current list and returning the sorted items as a new list,
287
+ according to any specified `key` and `reversed` arguments.
288
+
289
+ * `filter(predicate: callable = None, **filters: dict[str, object])` 🔗 (`fluentlist`)
290
+ – The `filter()` method supports filtering the contents of the current list in the two
291
+ ways noted below, and returns the filtered results as a new list:
292
+ - filtering can be performed via a `predicate` callable method that takes as input the
293
+ current item as the list is iterated over, where the `predicate` must return `True` for
294
+ items that should remain in the output, and `False` otherwise;
295
+ - alternatively, filtering can be performed via one or more keyword arguments, excepting
296
+ the reserved `predicate` keyword, which define the names and values of object attributes
297
+ that the item objects held in the list must match to be included in the output. Each item
298
+ in the list will be inspected to see if has the specified attribute (as per the keyword
299
+ argument name) and if so, if that attribute also has a value matching the value of the
300
+ keyword argument; for item objects that both have all of the specified attributes with
301
+ matching values, they will be included in the new list created by the filtering call,
302
+ otherwise they will be omitted.
303
+
304
+ * `first(predicate: callable = None, **filters: dict[str, object])` (`fluentlist`) –
305
+ The `first()` method supports returning the first item of the current list. Optionally,
306
+ the contents of the list can first be filtered according to the specified `predicate`
307
+ or `filtering` keyword argument values (as per those passed to the `filter()` method),
308
+ and then the first matching item will be returned. If the list is either empty or there
309
+ are no matches according to the specified filtering arguments, the method will return
310
+ `None`. As the method returns the first value in the current list or `None` if the list
311
+ is empty, the method cannot be chained onto, but can be as the last call on a chain of
312
+ other `fluentlist` methods that do support chaining.
313
+
314
+ * `last(predicate: callable = None, **filters: dict[str, object])` (`fluentlist`) –
315
+ The `last()` method supports returning the last item of the current list. Optionally,
316
+ the contents of the list can last be filtered according to the specified `predicate`
317
+ or `filtering` keyword argument values (as per those passed to the `filter()` method),
318
+ and then the last matching item will be returned. If the list is either empty or there
319
+ are no matches according to the specified filtering arguments, the method will return
320
+ `None`. As the method returns the last value in the current list or `None` if the list
321
+ is empty, the method cannot be chained onto, but can be as the last call on a chain of
322
+ other `fluentlist` methods that do support chaining.
323
+
324
+ #### Fluent List Operator Overrides
325
+
326
+ The `fluentlist` class also supports several operator overrides which provide some useful
327
+ and convenient behaviours for lists as noted in the table below:
328
+
329
+ | Operator | Value Type | Method Equivalent | Notes |
330
+ |:--------:|:----------:|-------------------|----------------------------------------------|
331
+ | `+` | `list` | `append(...)` | Creates a new list and appends to that list. |
332
+ | `+=` | `list` | `append(...)` | Appends in-place, updating the current list. |
333
+ | `-` | `object` | `remove(...)` | Creates a new list and removes in that list. |
334
+ | `-=` | `object` | `remove(...)` | Removes in-place, updating the current list. |
335
+ | `*` | `int` | `repeat(...)` | Creates a new list and repeats to that list. |
336
+ | `*=` | `int` | `repeat(...)` | Repeats in-place, updating the current list. |
337
+
338
+ The `+` and `+=` operator overloads can be used as follows:
339
+
340
+ ```python
341
+ from fluently import fluentlist
342
+
343
+ numbers = fluentlist([1, 2, 3])
344
+
345
+ # Using the `+` operator overload returns a new list into which the items are added
346
+ newnumbers = numbers + [4, 5, 6]
347
+
348
+ assert newnumbers == [1, 2, 3, 4, 5, 6]
349
+
350
+ # The `+=` operator overload appends items, returning the updated original list
351
+ numbers += [4, 5, 6]
352
+
353
+ assert numbers == [1, 2, 3, 4, 5, 6]
354
+ ```
355
+
356
+ The `-` and `-=` operator overloads can be used as follows:
357
+
358
+ ```python
359
+ from fluently import fluentlist
360
+
361
+ numbers = fluentlist([1, 2, 3])
362
+
363
+ # The `-` operator overload returns a new list from which the item is removed
364
+ newnumbers = numbers - 3 # Remove the first occurrence of `3` from the list
365
+
366
+ assert newnumbers == [1, 2]
367
+
368
+ # The `-=` operator overload removes the item, returning the updated original list
369
+ numbers -= 3 # Remove the first occurrence of `3` from the list
370
+
371
+ assert numbers == [1, 2]
372
+ ```
373
+
374
+ The `*` and `*=` operator overloads can be used as follows:
375
+
376
+ ```python
377
+ from fluently import fluentlist
378
+
379
+ numbers = fluentlist([1, 2, 3])
380
+
381
+ # The `*` operator overload, returning a new list into which the items are repeated
382
+ newnumbers = numbers * 2
383
+
384
+ assert newnumbers == [1, 2, 3, 1, 2, 3]
385
+
386
+ # The `*=` operator overload to repeats items, returning the updated original list
387
+ numbers *= 2
388
+
389
+ assert numbers == [1, 2, 3, 1, 2, 3]
390
+ ```
391
+
392
+ #### Fluent Set Methods
393
+
394
+ The `fluentset` class provides the following methods in addition to the methods provided
395
+ by the `set` superclass:
396
+
397
+ * `length()` (`int`) – The `length()` method supports returning the total count of items
398
+ in the current set. The method returns the count as an `int` value, so does not allow
399
+ further chaining, but can be used as the last call on chain of other `fluentset` methods
400
+ that do support chaining.
401
+
402
+ * `clone()` (`fluentset`) – The `clone()` method supports creating a cloned copy of the
403
+ current set, that contains the same items, in a separate `fluentset` instance.
404
+
405
+ * `add(item: object)` 🔗 (`fluentset`) – The `add()` method supports adding the specified
406
+ `item` to the current set, if a matching item has not already been added. This method
407
+ updates the current set in-place.
408
+
409
+ * `remove(item: object, raises: bool = True)` 🔗 (`fluentset`) – The `remove()` method
410
+ supports removing the specified `item` from the current set if the item is present in
411
+ the list. This method updates the current set in-place. If the item is not present, and
412
+ the optional `raises` keyword argument is set to its default of `True` then the method
413
+ will raise a `KeyError` exception will be raised noting the absence of the `item` value
414
+ in the set. If `raises` is set to `False` no exception will be raised, instead the error
415
+ will be logged, visible via the standard `logging` library if the log level is set to
416
+ `ERROR` or higher.
417
+
418
+ * `discard(item: object)` 🔗 (`fluentset`) – The `discard()` method supports removing
419
+ the specified `item` from the current set if the item is present in the list. No error
420
+ is raised if the `item` does not exist in the set.
421
+
422
+ * `clear()` 🔗 (`fluentset`) – The `clear()` method supports clearing all of the items
423
+ from the current set.
424
+
425
+ * `contains(item: object)` (`bool`) – The `contains()` method supports returning whether
426
+ the specified `item` value appears in the current set or not. The method returns a `bool`
427
+ value indicating the presence or absence of the specified `item` value, so does not allow
428
+ further chaining, but can be used as the last call on chain of other `fluentset` methods
429
+ that do support chaining.
430
+
431
+ #### Fluent Tuple Methods
432
+
433
+ The `fluenttuple` class provides the following methods in addition to the methods provided
434
+ by the `tuple` superclass:
435
+
436
+ * `length()` (`int`) – The `length()` method supports returning the total count of items
437
+ in the current tuple. The method returns the count as an `int` value, so does not allow
438
+ further chaining, but can be used as the last call on chain of other `fluenttuple` methods
439
+ that do support chaining.
440
+
441
+ * `clone()` (`fluenttuple`) – The `clone()` method supports creating a cloned copy of
442
+ the current tuple, that contains the same items, in a separate `fluenttuple` instance.
443
+
444
+ * `add(item: object)` 🔗 (`fluenttuple`) – The `add()` method supports appending the
445
+ specified `item` into a new tuple along with the values from the original tuple.
446
+
447
+ * `append(item: object)` 🔗 (`fluenttuple`) – The `append()` method supports appending
448
+ the specified `item` into a new tuple along with the values from the original tuple.
449
+
450
+ * `prepend(item: object)` 🔗 (`fluenttuple`) – The `append()` method supports prepending
451
+ the specified `item` into a new tuple along with the values from the original tuple.
452
+
453
+ * `remove(item: object, raises: bool = True)` 🔗 (`fluenttuple`) – The `remove()` method
454
+ supports removing the specified `item` from the current tuples values, returning the
455
+ resulting values as new tuple. If the item is not present, and the optional `raises`
456
+ keyword argument is tuple to its default of `True` then the method will raise a `ValueError`
457
+ exception will be raised noting the absence of the `item` value in the tuple. If `raises`
458
+ is tuple to `False` no exception will be raised, instead the error will be logged, visible
459
+ via the standard `logging` library if the log level is tuple to `ERROR` or higher.
460
+
461
+ * `discard(item: object)` 🔗 (`fluenttuple`) – The `discard()` method supports removing
462
+ the specified `item` from the current tuple if the item is present, returning the resulting
463
+ values as a new tuple. No error is raised if the `item` does not exist in the tuple.
464
+
465
+ * `clear()` 🔗 (`fluenttuple`) – The `clear()` method mimics clearing all of the items
466
+ from the current tuple, by returning a new empty `fluenttuple` instance.
467
+
468
+ * `repeat(count: int)` 🔗 (`fluenttuple`) – The `repeat()` method supports repeating all
469
+ the items from the current tuple by cloning the tuple values and appending the repeated item
470
+ values to the end of the new tuple, following the order in which they were originally defined.
471
+ The values are according to the specified `count` value. The method returns a new tuple.
472
+
473
+ * `reverse()` 🔗 (`fluenttuple`) – The `reverse()` method supports reversing the order
474
+ of the items from the current tuple into a new tuple.
475
+
476
+ * `shuffle()` 🔗 (`fluenttuple`) – The `shuffle()` method supports randomly shuffling all
477
+ of the items from the current tuple into a new tuple.
478
+
479
+ * `slice(start: int, stop: int = None, step: int = 1)` 🔗 (`fluenttuple`) – The `slice()`
480
+ method supports returning a slice of the items from the current tuple according to the
481
+ specified slice indices and step count. The method returns the slice within a new tuple;
482
+ it does not modify the original tuple.
483
+
484
+ * `take(index: int)` 🔗 (`fluenttuple`) – The `take()` method supports returning a slice
485
+ of the items from the current tuple from the beginning of the tuple at index 0 to the stop
486
+ index as specified by the provided `index` argument. The method returns the slice within
487
+ a new tuple; it does not modify the original tuple.
488
+
489
+ * `drop(index: int)` 🔗 (`fluenttuple`) – The `drop()` method supports returning a slice
490
+ of the items from the current tuple from the start index as specified by the provided
491
+ `index` argument until the end of the tuple. The method returns the slice within a new
492
+ tuple; it does not modify the original tuple.
493
+
494
+ * `swap(source: int, target: int)` 🔗 (`fluenttuple`) – The `swap()` method supports
495
+ swapping the items in the tuple at the specified `source` and `target` indices with each
496
+ other. The items are swapped into position within a new tuple, leaving the original untouched.
497
+
498
+ * `unique()` 🔗 (`fluenttuple`) – The `unique()` method supports providing a tuple of the
499
+ unique items from the current tuple. The method returns a new tuple containing the unique
500
+ items found in the current tuple; the original current tuple is not modified.
501
+
502
+ * `count(item: object)` (`int`) – The `count()` method supports providing a count of the
503
+ number of times the specified `item` value appears in the current tuple. The method returns
504
+ the count as an `int` value, so does not allow further chaining, but can be used as the
505
+ last call on chain of other `fluenttuple` methods that do support chaining.
506
+
507
+ * `contains(item: object)` (`bool`) – The `contains()` method supports returning whether
508
+ the specified `item` value appears in the current tuple at least once or not. The method
509
+ returns a `bool` value indicating the presence or absence of the specified `item` value,
510
+ so does not allow further chaining, but can be used as the last call on chain of other
511
+ `fluenttuple` methods that do support chaining.
512
+
513
+ * `any(item: object)` (`bool`) – The `any()` method supports returning whether the current
514
+ tuple contains the specified `item` value at least once or not. As the method returns a
515
+ `bool` value, it does not allow further chaining, but can be used as the last call on
516
+ chain of other `fluenttuple` methods that do support chaining.
517
+
518
+ * `all(item: object)` (`bool`) – The `all()` method supports returning whether the current
519
+ tuple solely contains the specified `item` value or not; that is for the `all()` method
520
+ to return `True`, the current tuple must only contain items that match the specified `item`
521
+ via the equality comparison `==` operator. As the method returns a `bool` value, it does
522
+ not allow further chaining, but can be used as the last call on chain of other `fluenttuple`
523
+ methods that do support chaining.
524
+
525
+ * `map(function: callable)` 🔗 (`fluenttuple`) – The `map()` method supports running the
526
+ specified `function` on each item in the current tuple. The method returns a new tuple
527
+ containing the result of running the `function` on each item in the current tuple rather
528
+ than modifying the current tuple in-place.
529
+
530
+ * `reduce(function: callable)` (`object`) – The `reduce()` method supports running the
531
+ specified `function` on each item in the current tuple, reducing the tuple down to a single
532
+ value, which the method returns upon completion. The method therefore does not support
533
+ chaining but can be as the last call on a chain of other `fluenttuple` methods that do
534
+ support chaining.
535
+
536
+ * `sort(key: object = None, reversed: bool = False)` 🔗 (`fluenttuple`) – The `sort()` method
537
+ supports sorting the current tuple in-place, according to any specified `key` and `reversed`
538
+ arguments. The contents of the current tuple will be updated to reflect the specified sort.
539
+
540
+ * `sorted(key: object = None, reversed: bool = False)` 🔗 (`fluenttuple`) – The `sorted()`
541
+ method supports sorting the current tuple and returning the sorted items as a new tuple,
542
+ according to any specified `key` and `reversed` arguments.
543
+
544
+ * `filter(predicate: callable = None, **filters: dict[str, object])` 🔗 (`fluenttuple`)
545
+ – The `filter()` method supports filtering the contents of the current tuple in the two
546
+ ways noted below, and returns the filtered results as a new tuple:
547
+ - filtering can be performed via a `predicate` callable method that takes as input the
548
+ current item as the tuple is iterated over, where the `predicate` must return `True` for
549
+ items that should remain in the output, and `False` otherwise;
550
+ - alternatively, filtering can be performed via one or more keyword arguments, excepting
551
+ the reserved `predicate` keyword, which define the names and values of object attributes
552
+ that the item objects held in the tuple must match to be included in the output. Each item
553
+ in the tuple will be inspected to see if has the specified attribute (as per the keyword
554
+ argument name) and if so, if that attribute also has a value matching the value of the
555
+ keyword argument; for item objects that both have all of the specified attributes with
556
+ matching values, they will be included in the new tuple created by the filtering call,
557
+ otherwise they will be omitted.
558
+
559
+ * `first(predicate: callable = None, **filters: dict[str, object])` (`fluenttuple`) –
560
+ The `first()` method supports returning the first item of the current tuple. Optionally,
561
+ the contents of the tuple can first be filtered according to the specified `predicate`
562
+ or `filtering` keyword argument values (as per those passed to the `filter()` method),
563
+ and then the first matching item will be returned. If the tuple is either empty or there
564
+ are no matches according to the specified filtering arguments, the method will return
565
+ `None`. As the method returns the first value in the current tuple or `None` if the tuple
566
+ is empty, the method cannot be chained onto, but can be as the last call on a chain of
567
+ other `fluenttuple` methods that do support chaining.
568
+
569
+ * `last(predicate: callable = None, **filters: dict[str, object])` (`fluenttuple`) –
570
+ The `last()` method supports returning the last item of the current tuple. Optionally,
571
+ the contents of the tuple can last be filtered according to the specified `predicate`
572
+ or `filtering` keyword argument values (as per those passed to the `filter()` method),
573
+ and then the last matching item will be returned. If the tuple is either empty or there
574
+ are no matches according to the specified filtering arguments, the method will return
575
+ `None`. As the method returns the last value in the current tuple or `None` if the tuple
576
+ is empty, the method cannot be chained onto, but can be as the last call on a chain of
577
+ other `fluenttuple` methods that do support chaining.
578
+
579
+ #### Fluent Tuple Operator Overrides
580
+
581
+ The `fluenttuple` class also supports several operator overrides which provide some useful
582
+ and convenient behaviours for tuples as noted in the table below:
583
+
584
+ | Operator | Value Type | Method Equivalent | Notes |
585
+ |:--------:|:----------:|-------------------|------------------------------------------------|
586
+ | `+` | `tuple` | `append(...)` | Creates a new tuple and appends to that tuple. |
587
+ | `+=` | `tuple` | `append(...)` | Appends in-place, updating the current tuple. |
588
+ | `-` | `object` | `remove(...)` | Creates a new tuple and removes in that tuple. |
589
+ | `-=` | `object` | `remove(...)` | Removes in-place, updating the current tuple. |
590
+ | `*` | `int` | `repeat(...)` | Creates a new tuple and repeats to that tuple. |
591
+ | `*=` | `int` | `repeat(...)` | Repeats in-place, updating the current tuple. |
592
+
593
+ The `+` and `+=` operator overloads can be used as follows:
594
+
595
+ ```python
596
+ from fluently import fluenttuple
597
+
598
+ numbers = fluenttuple([1, 2, 3])
599
+
600
+ # The `+` operator overload returns a new tuple to which the items are added
601
+ newnumbers = numbers + (4, 5, 6)
602
+
603
+ assert newnumbers == (1, 2, 3, 4, 5, 6)
604
+
605
+ # The `+=` operator overload to append items, returning a new tuple
606
+ numbers += (4, 5, 6)
607
+
608
+ assert numbers == (1, 2, 3, 4, 5, 6)
609
+ ```
610
+
611
+ The `-` and `-=` operator overloads can be used as follows:
612
+
613
+ ```python
614
+ from fluently import fluenttuple
615
+
616
+ numbers = fluenttuple([1, 2, 3])
617
+
618
+ # The `-` operator overload returns a new tuple from which the item is removed
619
+ newnumbers = numbers - 3 # Remove the first occurrence of `3` from the tuple
620
+
621
+ assert newnumbers == (1, 2)
622
+
623
+ # The `-=` operator overload removes the item, returning a new tuple
624
+ numbers -= 3 # Remove the first occurrence of `3` from the tuple
625
+
626
+ assert numbers == (1, 2)
627
+ ```
628
+
629
+ The `*` and `*=` operator overloads can be used as follows:
630
+
631
+ ```python
632
+ from fluently import fluenttuple
633
+
634
+ numbers = fluenttuple([1, 2, 3])
635
+
636
+ # The `*` operator overload repeats the items, returning a new tuple
637
+ newnumbers = numbers * 2
638
+
639
+ assert newnumbers == (1, 2, 3, 1, 2, 3)
640
+
641
+ # The `*=` operator overload to repeats the items into a new tuple
642
+ numbers *= 2
643
+
644
+ assert numbers == (1, 2, 3, 1, 2, 3)
645
+ ```
646
+
647
+ ### Unit Tests
648
+
649
+ The Fluently library includes a suite of comprehensive unit tests which ensure that the
650
+ library functionality operates as expected. The unit tests were developed with and are
651
+ run via `pytest`.
652
+
653
+ To ensure that the unit tests are run within a predictable runtime environment where all of the necessary dependencies are available, a [Docker](https://www.docker.com) image is created within which the tests are run. To run the unit tests, ensure Docker and Docker Compose is [installed](https://docs.docker.com/engine/install/), and perform the following commands, which will build the Docker image via `docker compose build` and then run the tests via `docker compose run` – the output of running the tests will be displayed:
654
+
655
+ ```shell
656
+ $ docker compose build
657
+ $ docker compose run tests
658
+ ```
659
+
660
+ To run the unit tests with optional command line arguments being passed to `pytest`, append the relevant arguments to the `docker compose run tests` command, as follows, for example passing `-vv` to enable verbose output:
661
+
662
+ ```shell
663
+ $ docker compose run tests -vv
664
+ ```
665
+
666
+ See the documentation for [PyTest](https://docs.pytest.org/en/latest/) regarding available optional command line arguments.
667
+
668
+ ### Copyright & License Information
669
+
670
+ Copyright © 2025 Daniel Sissman; licensed under the MIT License.