fluentpy

To use this module just import it with a short custom name. I recommend:

>>> import fluentpy as _ # for scripts / projects that don't use gettext
>>> import fluentpy as _f # for everything else

If you want / need this to be less magical, you can import the main wrapper normally

>>> from fluentpy import wrap # or `_`, if you're not using gettext

Then to use the module, wrap any value and start chaining off of it. To get started lets try to introspect fluentpy using its own fluent interface:

$ python3 -m fluentpy '_(_).dir().print()'
$ python3 -m fluentpy '_(_).help()'

This is incidentally the second way to use this module, as a helper that makes it easier to write short shell filters quickly in python.:

$ echo "foo\nbar\nbaz" \
    | python3 -m fluentpy "lib.sys.stdin.readlines().map(each.call.upper()).map(print)"

Try to rewrite that in classical python (as a one line shell filter) and see which version spells out what happens in which order more clearly.

For further documentation and development see this documentation or the source at https://github.com/dwt/fluent

Functions

  • wrap(): Factory method, wraps anything and returns the appropriate Wrapper subclass.

fluentpy.wrap(wrapped, *, previous=None)[source]

Factory method, wraps anything and returns the appropriate Wrapper subclass.

This is the main entry point into the fluent wonderland. Wrap something and everything you call off of that will stay wrapped in the appropriate wrappers.

It is usually imported in one of the following ways:

>>> import fluentpy as _
>>> import fluentpy as _f
>>> from fluentpy import wrap

wrap is the original name of the function, though I rarely recommend to use it by this name.

Classes

class fluentpy.Wrapper(wrapped, *, previous)[source]

Universal wrapper.

This class ensures that all function calls and attribute accesses (apart from such special CPython runtime accesses like object.__getattribute__, which cannot be intercepted) will be wrapped with the wrapper again. This ensures that the fluent interface will persist and everything that is returned is itself able to be chained from again.

All returned objects will be wrapped by this class or one of its sub classes, which add functionality depending on the type of the wrapped object. I.e. iterables will gain the collection interface, mappings will gain the mapping interface, strings will gain the string interface, etc.

If you want to access the actual wrapped object, you will have to unwrap it explicitly using .unwrap or ._

Please note: Since most of the methods on these objects are actual standard library methods that are simply wrapped to rebind their (usually first) parameter to the object they where called on. So for example: repr(something) becomes _(something).repr(). This means that the (unchanged) documentation (often) still shows the original signature and refers to the original arguments. A little bit of common sense might therefore be required.

Inheritance

Inheritance diagram of Wrapper
property unwrap

Returns the underlying wrapped value of this wrapper instance.

All other functions return wrappers themselves unless explicitly stated.

Alias: _

property previous

Returns the previous wrapper in the chain of wrappers.

This allows you to walk the chain of wrappers that where created in your expression. Mainly used internally but might be useful for introspection.

property self

Returns the previous wrapped object. This is especially usefull for APIs that return None.

For example _([1,3,2]).sort().self.print() will print the sorted list, even though sort() did return None.

This is simpler than using .previous as there are often multiple wrappers involved where you might expect only one. E.g. _([2,1]).sort().self._ == [1,2] but _([2,1]).sort().previous._ will return the function list.sort() as the attrget and call are two steps of the call chain.

This eases chaining using APIs that where not designed with chaining in mind. (Inspired by SmallTalk’s default behaviour)

property proxy

Allow access to shadowed attributes.

Breakout that allows access to attributes of the wrapped object that are shadowed by methods on the various wrapper classes. Wrapped of course.

>>> class UnfortunateNames(object):
>>>     def previous(self, *args):
>>>         return args

This raises TypeError, because Wrapper.previous() shadows UnfortunateNames.previous():

>>> _(UnfortunateNames()).previous('foo')) 

This works as expected:

>>> _(UnfortunateNames()).proxy.previous('foo')._) == ('foo',)
call(function, *args, **kwargs)[source]

Call function with self as its first argument.

>>> _('foo').call(list)._ == list('foo')
>>> _('fnord').call(textwrap.indent, prefix='  ')._ == textwrap.indent('fnord', prefix='  ')

Call is mostly usefull to insert normal functions that express some algorithm into the call chain. For example like this:

>>> seen = set()
>>> def havent_seen(number):
...     if number in seen:
...         return False
...     seen.add(number)
...     return True
>>> (
...     _([1,3,1,3,4,5,4])
...     .dropwhile(havent_seen)
...     .print()
... )

Less obvious, it can also be used as a decorator, however the result can be confusing, so maybe not as recomended:

>>> numbers = _(range(5))
>>> @numbers.call
... def items(numbers):
...     for it in numbers:
...         yield it
...         yield it
>>> items.call(list).print()

Note the difference from .__call__(). This applies function(self, …) instead of self(…).

to(function, *args, **kwargs)[source]

Like .call() but returns an unwrapped result.

This makes .to() really convenient to terminate a call chain by converting to a type that perhaps itself con.

setattr(name, value, /)

Sets the named attribute on the given object to the specified value.

setattr(x, ‘y’, v) is equivalent to ``x.y = v’’

getattr(object, name[, default]) value

Get a named attribute from an object; getattr(x, ‘y’) is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn’t exist; without it, an exception is raised in that case.

hasattr(name, /)

Return whether the object has an attribute with the given name.

This is done by calling getattr(obj, name) and catching AttributeError.

delattr(name, /)

Deletes the named attribute from the given object.

delattr(x, ‘y’) is equivalent to ``del x.y’’

isinstance(class_or_tuple, /)

Return whether an object is an instance of a class or of a subclass thereof.

A tuple, as in isinstance(x, (A, B, ...)), may be given as the target to check against. This is equivalent to isinstance(x, A) or isinstance(x, B) or ... etc.

issubclass(class_or_tuple, /)

Return whether ‘cls’ is a derived from another class or is the same class.

A tuple, as in issubclass(x, (A, B, ...)), may be given as the target to check against. This is equivalent to issubclass(x, A) or issubclass(x, B) or ... etc.

dir([object]) list of strings

If called without an argument, return the names in the current scope. Else, return an alphabetized list of names comprising (some of) the attributes of the given object, and of attributes reachable from it. If the object supplies a method named __dir__, it will be used; otherwise the default dir() logic is used and returns:

for a module object: the module’s attributes. for a class object: its attributes, and recursively the attributes

of its bases.

for any other object: its attributes, its class’s attributes, and

recursively the attributes of its class’s base classes.

vars([object]) dictionary

Without arguments, equivalent to locals(). With an argument, equivalent to object.__dict__.

print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)

Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments: file: a file-like object (stream); defaults to the current sys.stdout. sep: string inserted between values, default a space. end: string appended after the last value, default a newline. flush: whether to forcibly flush the stream.

pprint(stream=None, indent=1, width=80, depth=None, *, compact=False)

Pretty-print a Python object to a stream [default is sys.stdout].

help

Define the built-in ‘help’. This is a wrapper around pydoc.help (with a twist).

type = <function type>
str = <function str>
repr()

Return the canonical string representation of the object.

For many object types, including most builtins, eval(repr(obj)) == obj.

class fluentpy.ModuleWrapper(wrapped, *, previous)[source]

The Wrapper for modules transforms attribute accesses into pre-wrapped imports of sub-modules.

Inheritance

Inheritance diagram of ModuleWrapper
reload()

Reload the module and return it.

The module must have been successfully imported before.

class fluentpy.CallableWrapper(wrapped, *, previous)[source]

The Wrapper for callables adds higher order methods.

Inheritance

Inheritance diagram of CallableWrapper
curry(*default_args, **default_kwargs)[source]

Like functools.partial, but with a twist.

If you use wrap or _ as a positional argument, upon the actual call, arguments will be left-filled for those placeholders.

>>> _(operator.add).curry(_, 'foo')('bar')._ == 'barfoo'

If you use wrap._$NUMBER (with $NUMBER < 10) you can take full control over the ordering of the arguments.

>>> _(a_function).curry(_._0, _._0, _.7)

This will repeat the first argument twice, then take the 8th and ignore all in between.

You can also mix numbered with generic placeholders, but since it can be hard to read, I would not advise it.

There is also _._args which is the placeholder for the *args variable argument list specifier. (Note that it is only supported in the last position of the positional argument list.)

>>> _(lambda x, y=3: x).curry(_._args)(1, 2)._ == (1, 2)
>>> _(lambda x, y=3: x).curry(x=_._args)(1, 2)._ == (1, 2)
compose(outer)[source]

Compose two functions.

>>>  inner_function.compose(outer_function) \
...    == lambda *args, **kwargs: outer_function(inner_function(*args, **kwargs))
class fluentpy.IterableWrapper(wrapped, *, previous)[source]

The Wrapper for iterables adds iterator methods to any iterable.

Most iterators in Python 3 return an iterator by default, which is very interesting if you want to build efficient processing pipelines, but not so hot for quick and dirty scripts where you have to wrap the result in a list() or tuple() all the time to actually get at the results (e.g. to print them) or to actually trigger the computation pipeline.

Thus all iterators on this class are by default immediate, i.e. they don’t return the iterator but instead consume it immediately and return a tuple. Of course if needed, there is also an i{map,zip,enumerate,…} version for your enjoyment that returns the iterator.

Iterating over wrapped iterators yields unwrapped elements by design. This is neccessary to make fluentpy interoperable with the standard library. This means you will have to rewrap occasionally in handwritten iterator methods or when iterating over a wrapped iterator

Where methods return infinite iterators, the non i-prefixed method name is skipped. See icycle for an an example.

Inheritance

Inheritance diagram of IterableWrapper
iter(iterable) iterator
iter(callable, sentinel) iterator

Get an iterator from an object. In the first form, the argument must supply its own iterator, or be a sequence. In the second form, the callable is called until it returns the sentinel.

star_call(function, *args, **kwargs)[source]

Calls function(*self), but allows to prepend args and add kwargs.

get(target_index, default=<object object>)[source]

Like `dict.get() but for IterableWrappers and able to deal with generators

join(with_what='')[source]

Like str.join, but the other way around. Bohoo!

Also calls str on all elements of the collection before handing it off to str.join as a convenience.

freeze = <function tuple>
len()[source]

Just like len(), but also works for iterators.

Beware, that it has to consume the iterator to compute its length

max(iterable, *[, default=obj, key=func]) value
max(arg1, arg2, *args, *[, key=func]) value

With a single iterable argument, return its biggest item. The default keyword-only argument specifies an object to return if the provided iterable is empty. With two or more arguments, return the largest argument.

min(iterable, *[, default=obj, key=func]) value
min(arg1, arg2, *args, *[, key=func]) value

With a single iterable argument, return its smallest item. The default keyword-only argument specifies an object to return if the provided iterable is empty. With two or more arguments, return the smallest argument.

sum(start=0, /)

Return the sum of a ‘start’ value (default: 0) plus an iterable of numbers

When the iterable is empty, return the start value. This function is intended specifically for use with numeric values and may reject non-numeric types.

any()

Return True if bool(x) is True for any x in the iterable.

If the iterable is empty, return False.

all()

Return True if bool(x) is True for all values x in the iterable.

If the iterable is empty, return True.

reduce(function, sequence[, initial]) value

Apply a function of two arguments cumulatively to the items of a sequence, from left to right, so as to reduce the sequence to a single value. For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates ((((1+2)+3)+4)+5). If initial is present, it is placed before the items of the sequence in the calculation, and serves as a default when the sequence is empty.

ieach(a_function)[source]

call a_function on each elment in self purely for the side effect, then yield the input element.

each(a_function)

call a_function on each elment in self purely for the side effect, then yield the input element.

imap = <function map>
map = <function map>
istar_map = <function starmap>
istarmap = <function starmap>
star_map = <function starmap>
starmap = <function starmap>
ifilter = <function filter>
filter = <function filter>
ienumerate = <function enumerate>
enumerate = <function enumerate>
ireversed = <function reversed>
reversed = <function reversed>
isorted(*, key=None, reverse=False)

Return a new list containing all items from the iterable in ascending order.

A custom key function can be supplied to customize the sort order, and the reverse flag can be set to request the result in descending order.

sorted(*, key=None, reverse=False)

Return a new list containing all items from the iterable in ascending order.

A custom key function can be supplied to customize the sort order, and the reverse flag can be set to request the result in descending order.

igrouped(group_length)[source]

Cut self into tupels of length group_length s -> (s0,s1,s2,…sn-1), (sn,sn+1,sn+2,…s2n-1), (s2n,s2n+1,s2n+2,…s3n-1), …

grouped(group_length)

Cut self into tupels of length group_length s -> (s0,s1,s2,…sn-1), (sn,sn+1,sn+2,…s2n-1), (s2n,s2n+1,s2n+2,…s3n-1), …

izip = <function zip>
zip = <function zip>
iflatten(level=inf, stop_at_types=(<class 'str'>, <class 'bytes'>))[source]

Modeled after rubys array.flatten @see http://ruby-doc.org/core-1.9.3/Array.html#method-i-flatten

Calling flatten on string likes would lead to infinity recursion, thus @arg stop_at_types. If you want to flatten those, use a combination of @arg level and @arg stop_at_types.

flatten(level=inf, stop_at_types=(<class 'str'>, <class 'bytes'>))

Modeled after rubys array.flatten @see http://ruby-doc.org/core-1.9.3/Array.html#method-i-flatten

Calling flatten on string likes would lead to infinity recursion, thus @arg stop_at_types. If you want to flatten those, use a combination of @arg level and @arg stop_at_types.

ireshape(*spec)[source]

Creates structure from linearity.

Allows you to turn (1,2,3,4) into ((1,2),(3,4)). Very much inspired by numpy.reshape. @see https://docs.scipy.org/doc/numpy/reference/generated/numpy.reshape.html

@argument spec integer of tuple of integers that give the spec for the dimensions of the returned structure. The last dimension is inferred as needed. For example:

>>> _([1,2,3,4]).reshape(2)._ == ((1,2),(3,4))

Please note that

>>> _([1,2,3,4]).reshape(2,2)._ == (((1,2),(3,4)),)

The extra tuple around this is due to the specification being, two tuples of two elements which is possible exactly once with the given iterable.

This iterator will not ensure that the shape you give it will generate fully ‘rectangular’. This means that the last element in the generated sequnce the number of elements can be different! This tradeoff is made, so it works with infinite sequences.

reshape(*spec)

Creates structure from linearity.

Allows you to turn (1,2,3,4) into ((1,2),(3,4)). Very much inspired by numpy.reshape. @see https://docs.scipy.org/doc/numpy/reference/generated/numpy.reshape.html

@argument spec integer of tuple of integers that give the spec for the dimensions of the returned structure. The last dimension is inferred as needed. For example:

>>> _([1,2,3,4]).reshape(2)._ == ((1,2),(3,4))

Please note that

>>> _([1,2,3,4]).reshape(2,2)._ == (((1,2),(3,4)),)

The extra tuple around this is due to the specification being, two tuples of two elements which is possible exactly once with the given iterable.

This iterator will not ensure that the shape you give it will generate fully ‘rectangular’. This means that the last element in the generated sequnce the number of elements can be different! This tradeoff is made, so it works with infinite sequences.

igroupby = <function groupby>
groupby(*args, **kwargs)[source]

See igroupby for most of the docs.

Correctly consuming an itertools.groupby is surprisingly hard, thus this non tuple returning version that does it correctly.

itee()

tee(iterable, n=2) –> tuple of n independent iterators.

islice = <function islice>
slice = <function islice>
icycle = <function cycle>
iaccumulate = <function accumulate>
accumulate = <function accumulate>
idropwhile = <function dropwhile>
dropwhile = <function dropwhile>
ifilterfalse = <function filterfalse>
filterfalse = <function filterfalse>
ipermutations = <function permutations>
permutations = <function permutations>
icombinations = <function combinations>
combinations = <function combinations>
icombinations_with_replacement = <function combinations_with_replacement>
combinations_with_replacement = <function combinations_with_replacement>
iproduct = <function product>
product = <function product>
class fluentpy.MappingWrapper(wrapped, *, previous)[source]

The Wrapper for mappings allows indexing into mappings via attribute access.

Indexing into dicts like objects. As JavaScript can.

>>> _({ 'foo': 'bar: }).foo == 'bar

Inheritance

Inheritance diagram of MappingWrapper
star_call(function, *args, **kwargs)[source]

Calls function(**self), but allows to add args and set defaults for kwargs.

class fluentpy.SetWrapper(wrapped, *, previous)[source]

The Wrapper for sets is mostly like IterableWrapper.

Mostly like IterableWrapper

Inheritance

Inheritance diagram of SetWrapper
freeze = <function frozenset>
class fluentpy.TextWrapper(wrapped, *, previous)[source]

The Wrapper for str adds regex convenience methods.

Supports most of the regex methods as if they where native str methods

Inheritance

Inheritance diagram of TextWrapper
search(string, flags=0)

Scan through string looking for a match to the pattern, returning a match object, or None if no match was found.

match(string, flags=0)

Try to apply the pattern at the start of the string, returning a match object, or None if no match was found.

fullmatch(string, flags=0)

Try to apply the pattern at the start of the string, returning a match object, or None if no match was found.

split(string, maxsplit=0, flags=0)

Split the source string by the occurrences of the pattern, returning a list containing the resulting substrings. If capturing parentheses are used in pattern, then the text of all groups in the pattern are also returned as part of the resulting list. If maxsplit is nonzero, at most maxsplit splits occur, and the remainder of the string is returned as the final element of the list.

findall(string, flags=0)

Return a list of all non-overlapping matches in the string.

If one or more capturing groups are present in the pattern, return a list of groups; this will be a list of tuples if the pattern has more than one group.

Empty matches are included in the result.

finditer(string, flags=0)

Return an iterator over all non-overlapping matches in the string. For each match, the iterator returns a match object.

Empty matches are included in the result.

sub(repl, string, count=0, flags=0)

Return the string obtained by replacing the leftmost non-overlapping occurrences of the pattern in string by the replacement repl. repl can be either a string or a callable; if a string, backslash escapes in it are processed. If it is a callable, it’s passed the match object and must return a replacement string to be used.

subn(repl, string, count=0, flags=0)

Return a 2-tuple containing (new_string, number). new_string is the string obtained by replacing the leftmost non-overlapping occurrences of the pattern in the source string by the replacement repl. number is the number of substitutions that were made. repl can be either a string or a callable; if a string, backslash escapes in it are processed. If it is a callable, it’s passed the match object and must return a replacement string to be used.

int = <function int>
float = <function float>
ord()

Return the Unicode code point for a one-character string.

class fluentpy.EachWrapper(operation, name)[source]

The Wrapper for expressions (see documentation for each).

Inheritance

Inheritance diagram of EachWrapper
in_(haystack)[source]

Implements a method version of the in operator.

So _.each.in_('bar') is roughly equivalent to lambda each: each in 'bar'

not_in(haystack)[source]

Implements a method version of the not in operator.

So _.each.not_in('bar') is roughly equivalent to lambda each: each not in 'bar'

Variables

fluentpy.lib

Imports as expressions. Already pre-wrapped.

All attribute accesses to instances of this class are converted to an import statement, but as an expression that returns the wrapped imported object.

Example:

>>> lib.sys.stdin.read().map(print)

Is equivalent to

>>> import sys
>>> wrap(sys).stdin.read().map(print)

But of course without creating the intermediate symbol ‘stdin’ in the current namespace.

All objects returned from lib are pre-wrapped, so you can chain off of them immediately.

fluentpy.wrap('virtual root module')
fluentpy.each

Create functions from expressions.

Use each.foo._ to create attrgetters, each['foo']._ to create itemgetters, each.foo()._ to create methodcallers or each == 'foo' (with pretty much any operator) to create callable operators.

Many operations can be chained, to extract a deeper object by iterating a container. E.g.:

>>> each.foo.bar('baz')['quoox']._

creates a callable that will first get the attribute foo, then call the method bar('baz') on the result and finally applies gets the item ‘quoox’ from the result. For this reason, all callables need to be unwrapped before they are used, as the actual application would just continue to build up the chain on the original callable otherwise.

Apply an operator like each < 3 to generate a callable that applies that operator. Different to all other cases, applying any binary operator auto terminates the expression generator, so no unwrapping is neccessary.

Note: The generated functions never wrap their arguments or return values.

fluentpy.wrap(each)
fluentpy._0

Placeholder for CallableWrapper.curry() to access argument at index 0.

fluentpy.wrap(0)
fluentpy._1

Placeholder for CallableWrapper.curry() to access argument at index 1.

fluentpy.wrap(1)
fluentpy._2

Placeholder for CallableWrapper.curry() to access argument at index 2.

fluentpy.wrap(2)
fluentpy._3

Placeholder for CallableWrapper.curry() to access argument at index 3.

fluentpy.wrap(3)
fluentpy._4

Placeholder for CallableWrapper.curry() to access argument at index 4.

fluentpy.wrap(4)
fluentpy._5

Placeholder for CallableWrapper.curry() to access argument at index 5.

fluentpy.wrap(5)
fluentpy._6

Placeholder for CallableWrapper.curry() to access argument at index 6.

fluentpy.wrap(6)
fluentpy._7

Placeholder for CallableWrapper.curry() to access argument at index 7.

fluentpy.wrap(7)
fluentpy._8

Placeholder for CallableWrapper.curry() to access argument at index 8.

fluentpy.wrap(8)
fluentpy._9

Placeholder for CallableWrapper.curry() to access argument at index 9.

fluentpy.wrap(9)
fluentpy._args

The Wrapper for str adds regex convenience methods.

Supports most of the regex methods as if they where native str methods

fluentpy.wrap('*')