fabulist.lorem_ipsum — fabulist 1.2.0 documentation

# Source code for fabulist.lorem_ipsum

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    """
    (c) 2017 Martin Wendt; see https://github.com/mar10/fabulist

    Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
    """
    from __future__ import print_function

    import os
    import random

    def _get_count(int_or_range):
        """Return random int for given range (or int if a simple value was passed)."""
        if type(int_or_range) is int:
            return int_or_range
        return random.randint(*int_or_range)

    # -------------------------------------------------------------------------------------------------
    # LoremDialect
    # -------------------------------------------------------------------------------------------------
    class LoremDialect(object):
        """Generate lorem ipsum text.

        Args:
            dialect (str): "lorem", "pulp", ...
            path (str):
        Examples:
            $(TYPE:MODS:#foo|bar:=NUM)
        """
        def __init__(self, dialect, path):
            self.dialect = dialect
            self.path = path
            self.paragraphs = None
            self.sentences = None
            self.words = None
            # self.load()

        def load(self):
            sentence_set = set()
            self.paragraphs = []
            self.sentences = []
            self.words = set()
            para = []
            for line in open(self.path, "rt"):
                # Skip empty lines and comments (i.e. starting with '#')
                line = line.strip()
                if not line or line.startswith("#"):
                    continue
                # Paragraphs are delimited by a '---' line
                if line.startswith("---"):
                    self.paragraphs.append(para)
                    para = []
                else:
                    para.append(line)
                    # Also collect a flat list of all sentences
                    if line not in sentence_set:
                        self.sentences.append(line)
                        sentence_set.add(line)
                        # Also collect a set of words
                        for word in line.split(" "):
                            word = word.strip(" \t\n,.!?;:-").lower()
                            if word:
                                self.words.add(word)
            self.words = list(self.words)
            if para:
                self.paragraphs.append(para)
            return

        def _generate_sentences(self, entropy=0, keep_first=True, count=None):
            """Generate a sequence of sentences.

            Args:
                entropy (int, optional):
                    0: generate sentences in original order
                    1: pick random paragraph, then use sentences in order
                    2: pick random sentence
                    Default: 0.
                keep_first (bool, optional):
                    Always return the words of the first sentence as first result.
                    Default: False.
                count (int):
                    Number of words.
            Yields:
                str: Random word.
            """
            if self.paragraphs is None:
                self.load()

            pool_idx = 0
            pool_remain = 0
            sentence_pool = self.sentences

            n_sentences = 0
            while count is None or n_sentences < count:
                if entropy == 1 and pool_remain == 0:
                    # Pick random paragraph, then use sentences in order
                    sentence_pool = random.choice(self.paragraphs)
                    pool_remain = len(sentence_pool)
                    pool_idx = 0

                if entropy == 2:
                    # Pick random sentence
                    sentence = random.choice(sentence_pool)
                else:
                    # Generate sentences in original order
                    sentence = sentence_pool[pool_idx % len(sentence_pool)]

                if keep_first:
                    keep_first = False
                    if sentence != self.sentences[0]:
                        # Start with first sentence
                        yield self.sentences[0]
                        n_sentences += 1

                pool_idx += 1
                pool_remain -= 1
                n_sentences += 1

                yield sentence
            return

    # -------------------------------------------------------------------------------------------------
    # LoremGenerator
    # -------------------------------------------------------------------------------------------------
    class LoremGenerator(object):
        """Generate lorem ipsum text in a given dialect.

        Attributes:
            dialect_map (dict(dialect, LoremDialect)):
                Holds all available lorem-ipsum dialects
        """
        def __init__(self, data_folder):
            self.dialect_map = {}
            self.root_path = data_folder
            # Find all available dialects and add to map(dialect => path)
            for name in os.listdir(self.root_path):
                if name.startswith("lorem_"):
                    dialect = os.path.splitext(name)[0][6:]
                    path = os.path.join(self.root_path, name)
                    self.dialect_map[dialect] = LoremDialect(dialect, path)
            return

        def _get_lorem(self, dialect):
            """Return a LoremDialect instance and load data or raise ValueError."""
            if dialect is None:
                dialect = random.choice(list(self.dialect_map.keys()))
            lorem = self.dialect_map.get(dialect)
            if not lorem:
                raise ValueError(
                    "Unknown dialect {!r} (expected {})"
                    .format(dialect, ", ".join(self.dialect_map.keys())))
            if lorem.paragraphs is None:
                lorem.load()
            return lorem

        def generate_words(self, count=None, dialect="ipsum", entropy=3, keep_first=False):
            """Yield <count> random words.

            Args:
                count (int, optional):
                    Number of words. Pass None for infinite.
                    Default: None.
                dialect (str, optional):
                    For example "ipsum", "pulp", "trappatoni". Pass `None` to pick a random dialect.
                    Default: "ipsum" (i.e. lorem-ipsum).
                entropy (int, optional):
                    0: iterate words from original text
                    1: pick a random paragraph, then use it literally
                    2: pick a random sentence, then use it literally
                    3: pick random words
                    Default: 3.
                keep_first (bool, optional):
                    Always return the words of the first sentence as first result.
                    Default: False.
            Yields:
                str: Random word.
            """
            lorem = self._get_lorem(dialect)
            i = 0
            if entropy == 3:
                # Pick random words
                if keep_first:
                    raise NotImplementedError
                while count is None or i < count:
                    yield random.choice(lorem.words)
                    i += 1
                return

            # Otherwise pop words from sentence sequence
            for s in lorem._generate_sentences(keep_first=keep_first, entropy=entropy):
                for word in s.split(" "):
                    if count is not None and i >= count:
                        return
                    word = word.strip().rstrip(".!?:")
                    if word:
                        yield word.lower()
                        i += 1
            return

        def generate_sentences(
                self, count=None, dialect="ipsum", entropy=2, keep_first=False,
                words_per_sentence=(3, 15)):
            """Yield <count> random sentences.

            Args:
                count (int, optional):
                    Number of sentences. Pass None for infinite.
                    Default: None.
                dialect (str, optional):
                    For example "ipsum", "pulp", "trappatoni". Pass `None` to pick a random dialect.
                    Default: "ipsum" (i.e. lorem-ipsum).
                entropy (int, optional):
                    0: iterate sentences from original text
                    1: pick a random paragraph, then iterate sentences
                    2: pick a random sentence
                    3: mix random words
                    Default: 2.
                keep_first (bool, optional):
                    Always return the first sentence as first result.
                    Default: False.
                words_per_sentence (int or tuple(min, max), optional):
                    Number of words per sentence.
                    This argument is only used for entropy=3.
                    Default: (3, 15).
            Yields:
                str: Random sentence.
            """
            lorem = self._get_lorem(dialect)
            i = 0
            if entropy == 3:
                # Generate from random words
                if keep_first:
                    yield lorem.sentences[0]
                    i += 1

                if not words_per_sentence:
                    raise ValueError(
                        "entropy=3 requires words_per_sentence arg: int or a tuple(min, max)")

                while count is None or i < count:
                    n_words = _get_count(words_per_sentence)
                    sentence = random.sample(lorem.words, n_words)
                    sentence = " ".join(sentence).capitalize() + "."
                    yield sentence
                    i += 1
                return
            # entropy = 0..2: use sentences from original text
            for i, s in enumerate(lorem._generate_sentences(keep_first=keep_first, entropy=entropy)):
                if i >= count:
                    break
                yield s
            return

        def generate_paragraphs(
                self, count=None, dialect="ipsum", entropy=2, keep_first=False,
                words_per_sentence=(3, 15), sentences_per_para=(2, 6)):
            """Generate a number of paragraphs, made up from random sentences.

            Args:
                count (int, optional):
                    Number of paragraphs. Pass None for infinite.
                    Default: None.
                dialect (str, optional):
                    For example "ipsum", "pulp", "trappatoni". Pass `None` to pick a random dialect.
                    Default: "ipsum".
                keep_first (bool, optional):
                    Always return the first sentence as first result. Default: False.
                entropy (int):
                    0: iterate original text
                    1: pick a random paragraph, then use it literally
                    2: mix a random sentences
                    3: mix random words
                    Default: 2.
                words_per_sentence (int or tuple(min, max), optional):
                    Number of words per sentence.
                    This argument is only used for entropy=3.
                    Default: (3, 15).
                sentences_per_para (int or tuple(min, max), optional):
                    Number of sentences per paragraph.
                    Default: (2, 6).
            Yields:
                str: Random paragraph.
            """
            i = 0
            while count is None or i < count:
                n_sents = _get_count(sentences_per_para)
                para = self.generate_sentences(
                    n_sents, dialect, entropy, keep_first, words_per_sentence)
                para = " ".join(para)
                yield para
                i += 1
                keep_first = False
            return

    if __name__ == "__main__":

        data_folder = os.path.join(os.path.dirname(__file__), "data")
        lorem = LoremGenerator(data_folder)
        # print(list(lorem.generate_words(10)))
        # print(list(lorem.generate_words(10, entropy=0)))
        # print(list(lorem.generate_words(10, entropy=1)))
        # print(list(lorem.generate_words(10, entropy=2)))
        # print(list(lorem.generate_words(10, entropy=3)))
        #
        # print(list(lorem.generate_sentences(3, entropy=3, words_per_sentence=(5,8))))
        # print(list(lorem.generate_sentences(3, entropy=3, words_per_sentence=5)))
        # print(list(lorem.generate_sentences(3, entropy=3, words_per_sentence=5, keep_first=True)))
        #
        # print(list(lorem.generate_sentences(3, dialect="pulp", entropy=2, keep_first=True)))
        #
        #
        # print()
        # print("\n".join(lorem.generate_paragraphs(3)))
        # print()
        # print("\n".join(lorem.generate_paragraphs(3, dialect="pulp", entropy=2, keep_first=True)))
        # print()
        # print("\n".join(lorem.generate_paragraphs(3, dialect="pulp", entropy=3, keep_first=True)))

        exit()
        # s = lorem.generate(None, 20)
        # gen = lorem.dialect_map["pulp"].generate_sentences(3)
        gen = lorem.generate_sentences(10, keep_first=False, entropy=2)
        for s in gen:
            print("-", s)
        # s = lorem.generate("pulp", 20)
        # print(s)