Skip to main content

Functional-style Streams library for processing collections and querying files (json, toml, yaml, xml, csv, tsv). Provides easy integration with itertools.

Project description

Pyrio

PYRIO

Python 3.x tests codecov PyPI Downloads


Functional-style Streams API library

Facilitates processing of collections and iterables using fluent APIs.
Gives access to files of various types (json, toml, yaml, xml, csv and tsv) for reading and executing complex queries
Provides easy integration with itertools
(NB: Commonly used itertools 'recipes' are included as part of the main APIs)

How to use

Creating streams

  • stream from iterable
Stream([1, 2, 3])
  • from variadic arguments
Stream.of(1, 2, 3)
  • empty stream
Stream.empty()
  • infinite ordered stream
Stream.iterate(0, lambda x: x + 1)
  • infinite unordered stream
import random

Stream.generate(lambda: random.random())
  • infinite stream with given value
Stream.constant(42)
  • concat
    (concatenate several streams together or add new streams to the current one)
Stream.concat((1, 2, 3), [5, 6]).to_list()
Stream.of(1, 2, 3).concat([4, 5]).to_list()
  • prepend
    (prepend iterable to current stream)
Stream([2, 3, 4]).prepend(0, 1).to_list()

Intermediate operations

  • filter
Stream([1, 2, 3]).filter(lambda x: x % 2 == 0)
  • map
Stream([1, 2, 3]).map(str).to_list()
Stream([1, 2, 3]).map(lambda x: x + 5).to_list()
  • filter_map
    (filter out all None or falsy values (if falsy=True) and applies mapper function to the elements of the stream)
Stream.of(None, "foo", "", "bar", 0, []).filter_map(str.upper, falsy=True).to_list()
["FOO", "BAR"]
  • flat_map
    (map each element of the stream and yields the elements of the produced iterators)
Stream([[1, 2], [3, 4], [5]]).flat_map(lambda x: Stream(x)).to_list()
[1, 2, 3, 4, 5]
  • flatten
Stream([[1, 2], [3, 4], [5]]).flatten().to_list()
[1, 2, 3, 4, 5]
  • reduce
    (returns Optional)
Stream([1, 2, 3]).reduce(lambda acc, val: acc + val, identity=3).get()
  • peek
    (perform the provided operation on each element of the stream without consuming it)
(Stream([1, 2, 3, 4])
    .filter(lambda x: x > 2)
    .peek(lambda x: print(f"{x} ", end=""))
    .map(lambda x: x * 20)
    .to_list())
  • view
    (provides access to a selected part of the stream)
Stream([1, 2, 3, 4, 5, 6, 7, 8, 9]).view(start=1, stop=-3, step=2).to_list()
[2, 4, 6]
  • distinct
    (returns a stream with the distinct elements of the current one)
Stream([1, 1, 2, 2, 2, 3]).distinct().to_list()
  • skip
    (discards the first n elements of the stream and returns a new stream with the remaining ones)
Stream.iterate(0, lambda x: x + 1).skip(5).limit(5).to_list()
  • limit / head
    (returns a stream with the first n elements, or fewer if the underlying iterator ends sooner)
Stream([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).limit(3).to_tuple()
Stream([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).head(3).to_tuple()
  • tail
    (returns a stream with the last n elements, or fewer if the underlying iterator ends sooner)
Stream([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).tail(3).to_tuple()
  • take_while
    (returns a stream that yields elements based on a predicate)
Stream.of(1, 2, 3, 4, 5, 6, 7, 2, 3).take_while(lambda x: x < 5).to_list()
[1, 2, 3, 4]
  • drop_while
    (returns a stream that skips elements based on a predicate and yields the remaining ones)
Stream.of(1, 2, 3, 5, 6, 7, 2).drop_while(lambda x: x < 5).to_list()
[5, 6, 7, 2]
  • sorted
    (sorts the elements of the current stream according to natural order or based on the given comparator;
    if 'reverse' flag is True, the elements are sorted in descending order)
(Stream.of((3, 30), (2, 30), (2, 20), (1, 20), (1, 10))
    .sorted(lambda x: (x[0], x[1]), reverse=True)
    .to_list())
[(3, 30), (2, 30), (2, 20), (1, 20), (1, 10)]

Terminal operations

Collectors

  • collecting result into list, tuple, set
Stream([1, 2, 3]).to_list()
Stream([1, 2, 3]).to_tuple()
Stream([1, 2, 3]).to_set()
  • into dict
class Foo:
    def __init__(self, name, num):
        self.name = name
        self.num = num
        
Stream([Foo("fizz", 1), Foo("buzz", 2)]).to_dict(lambda x: (x.name, x.num))
{"fizz": 1, "buzz": 2}

In the case of a collision (duplicate keys) the 'merger' functions indicates which entry should be kept

collection = [Foo("fizz", 1), Foo("fizz", 2), Foo("buzz", 2)]
Stream(collection).to_dict(collector=lambda x: (x.name, x.num), merger=lambda old, new: old)
{"fizz": 1, "buzz": 2}
  • alternative for working with collectors is using the collect method
Stream([1, 2, 3]).collect(tuple)
Stream.of(1, 2, 3).collect(list)
Stream.of(1, 1, 2, 2, 2, 3).collect(set)
Stream.of(1, 2, 3, 4).collect(dict, lambda x: (str(x), x * 10))
  • grouping
Stream("AAAABBBCCD").group_by(collector=lambda key, grouper: (key, len(grouper)))
{"A": 4, "B": 3, "C": 2, "D": 1}
coll = [Foo("fizz", 1), Foo("fizz", 2), Foo("fizz", 3), Foo("buzz", 2), Foo("buzz", 3), Foo("buzz", 4), Foo("buzz", 5)]
Stream(coll).group_by(
    classifier=lambda obj: obj.name,
    collector=lambda key, grouper: (key, [(obj.name, obj.num) for obj in list(grouper)]))
{
  "fizz": [("fizz", 1), ("fizz", 2), ("fizz", 3)],
  "buzz": [("buzz", 2), ("buzz", 3), ("buzz", 4), ("buzz", 5)],
}

Other terminal operations

  • for_each
Stream([1, 2, 3, 4]).for_each(lambda x: print(f"{'#' * x} ", end=""))
  • count
    (returns the count of elements in the stream)
Stream([1, 2, 3, 4]).filter(lambda x: x % 2 == 0).count()
  • sum
Stream.of(1, 2, 3, 4).sum() 
  • find_first
    (search for an element of the stream that satisfies a predicate, returns an Optional with the first found value, if any, or None)
Stream.of(1, 2, 3, 4).filter(lambda x: x % 2 == 0).find_first().get()
  • find_any
    (search for an element of the stream that satisfies a predicate, returns an Optional with some of the found values, if any, or None)
Stream.of(1, 2, 3, 4).filter(lambda x: x % 2 == 0).find_any().get()
  • any_match
    (returns whether any elements of the stream match the given predicate)
Stream.of(1, 2, 3, 4).any_match(lambda x: x > 2)
  • all_match
    (returns whether all elements of the stream match the given predicate)
Stream.of(1, 2, 3, 4).all_match(lambda x: x > 2)
  • none_match
    (returns whether no elements of the stream match the given predicate)
Stream.of(1, 2, 3, 4).none_match(lambda x: x < 0)
  • min
    (returns Optional with the minimum element of the stream)
Stream.of(2, 1, 3, 4).min().get()
  • max
    (returns Optional with the maximum element of the stream)
Stream.of(2, 1, 3, 4).max().get()
  • compare_with
    (compares linearly the contents of two streams based on a given comparator)
fizz = Foo("fizz", 1)
buzz = Foo("buzz", 2)
Stream([buzz, fizz]).compare_with(Stream([fizz, buzz]), lambda x, y: x.num == y.num)
  • quantify
    (count how many of the elements are Truthy or evaluate to True based on a given predicate)
Stream([2, 3, 4, 5, 6]).quantify(predicate=lambda x: x % 2 == 0)

Itertools integration

Invoke use method by passing the itertools function and it's arguments as **kwargs

import itertools
import operator

Stream([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]).use(itertools.islice, start=3, stop=8)
Stream.of(1, 2, 3, 4, 5).use(itertools.accumulate, func=operator.mul).to_list()
Stream(range(3)).use(itertools.permutations, r=3).to_list()

Itertools 'recipes'

Invoke the 'recipes' described here as stream methods and pass required key-word arguments

Stream([1, 2, 3]).ncycles(count=2).to_list()
Stream.of(2, 3, 4).take_nth(10, default=66).get()
Stream(["ABC", "D", "EF"]).round_robin().to_list()

Querying files with FileStream

  • working with json, toml, yaml, xml files
    NB: FileStream reads data as series of key/value tuples from underlying dict_items view
    (This draw-back in the current API design is caused by the unfortunate removal of tuple parameter unpacking with PEP 3113)
FileStream("path/to/file").map(lambda x: f"{x[0]}=>{x[1]}").to_tuple()
(
  "abc=>xyz", 
  "qwerty=>42",
)
from operator import itemgetter

(FileStream("path/to/file")
    .filter(lambda x: "a" in x[0])
    .map(lambda x: (x[0], sum(x[1]) * 10))
    .sorted(itemgetter(1), reverse=True)
    .map(lambda x: f"{str(x[1])}::{x[0]}")
    .to_list()) 
["230::xza", "110::abba", "30::a"]
  • querying csv and tsv files
    (each row is read as a dict with keys taken from the header row)
FileStream("path/to/file").map(lambda x: f"fizz: {x['fizz']}, buzz: {x['buzz']}").to_tuple() 
(
  "fizz: 42, buzz: 45",
  "fizz: aaa, buzz: bbb",
)
from operator import itemgetter

FileStream("path/to/file").map(itemgetter('fizz', 'buzz')).to_tuple()
(('42', '45'), ('aaa', 'bbb'))

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyrio-1.2.1.tar.gz (14.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyrio-1.2.1-py3-none-any.whl (15.2 kB view details)

Uploaded Python 3

File details

Details for the file pyrio-1.2.1.tar.gz.

File metadata

  • Download URL: pyrio-1.2.1.tar.gz
  • Upload date:
  • Size: 14.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.12.3 Linux/6.8.0-48-generic

File hashes

Hashes for pyrio-1.2.1.tar.gz
Algorithm Hash digest
SHA256 ae2c41f53d9d4ad248113d5a0abeba654439d341d11129322811868481ec23be
MD5 a045f8e040f29e994d6e9c6a154bca1d
BLAKE2b-256 d69e301eb14d0441f172cbe47a7c35c6b3e6e8781145c55a7b58e36e7c8c3b65

See more details on using hashes here.

File details

Details for the file pyrio-1.2.1-py3-none-any.whl.

File metadata

  • Download URL: pyrio-1.2.1-py3-none-any.whl
  • Upload date:
  • Size: 15.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.12.3 Linux/6.8.0-48-generic

File hashes

Hashes for pyrio-1.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 81aed7e7318e2c31fb27922e140ceca8f51231254a14af1ba32eef10d2d00e89
MD5 fa2910dde2ddd997731e30f8693105e6
BLAKE2b-256 d6b9fc82c35a93aff9bb9be266d5e218fa75dc88edd0d92493355d8301f97609

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page