PrettyPrint - Documentation

Introduction

What is PrettyPrint?

PrettyPrint is a library designed to enhance the readability of various data structures and code by formatting them into a visually appealing and easily understandable representation. It supports a wide range of input types, including dictionaries, lists, trees, and even custom data structures, transforming them into neatly formatted strings that are suitable for logging, debugging, or simply displaying information to the user. The library prioritizes clarity and consistency in its output, making complex data easier to digest.

Why use PrettyPrint?

Using PrettyPrint offers several key advantages:

Installation and Setup

PrettyPrint can be easily installed using pip:

pip install prettyprint

No further setup is typically required. Simply import the library into your Python scripts to begin using its functionalities.

Basic Usage Example

This example demonstrates how to use PrettyPrint to format a simple Python dictionary:

from prettyprint import pp

data = {
    "name": "Example Data",
    "values": [1, 2, 3, 4, 5],
    "nested": {
        "key1": "value1",
        "key2": [True, False]
    }
}

pp(data)

This will output a formatted representation of the data dictionary to the console, similar to this (the exact formatting might vary slightly depending on your terminal):

{
    'name': 'Example Data',
    'values': [
        1,
        2,
        3,
        4,
        5
    ],
    'nested': {
        'key1': 'value1',
        'key2': [
            True,
            False
        ]
    }
}

The output clearly shows the structure of the dictionary and its nested elements, improving readability compared to a standard Python print() output.

Core Concepts

Data Structures

PrettyPrint natively supports a wide range of Python data structures, including but not limited to:

Formatting Options

While PrettyPrint automatically applies sensible formatting, you can influence the output using various parameters (though most cases require no explicit parameters). Future versions might offer more explicit configuration options. Currently, the primary control over formatting comes from the way the input data is structured and the behavior of the __repr__ and __str__ methods of any custom classes. For example, using lists instead of dictionaries will dramatically alter the final format.

Customizing Output

The most significant way to customize the output is by controlling the __repr__ or __str__ methods within your custom classes. By carefully crafting the string returned by these methods, you can dictate exactly how your objects are represented in the PrettyPrint output.

For example:

class MyCustomObject:
    def __init__(self, name, value):
        self.name = name
        self.value = value

    def __repr__(self):
        return f"<MyCustomObject(name='{self.name}', value={self.value})>"

my_object = MyCustomObject("Example", 123)
pp(my_object)

This will produce an output that reflects the custom __repr__ method: <MyCustomObject(name='Example', value=123)>. By designing informative __repr__ or __str__ methods, you ensure that your custom objects are displayed in a clear and contextually relevant manner. If you don’t define these methods, the default representation will be used, showing the class name and memory address.

Advanced Usage

Conditional Formatting

PrettyPrint doesn’t directly support conditional formatting based on data values within the library itself. Conditional formatting would typically be achieved by preprocessing the data before passing it to pp(). You can create functions to modify the data structure based on conditions, adding formatting information (e.g., extra strings or specific characters) to indicate different states. This modified data would then be passed to PrettyPrint for its standard formatting.

For example, to highlight values above a certain threshold:

from prettyprint import pp

data = {'a': 10, 'b': 20, 'c': 5}

def highlight_above_threshold(data, threshold=15):
    for key, value in data.items():
        if value > threshold:
            data[key] = f"**{value}**"  # Add visual cues
    return data

formatted_data = highlight_above_threshold(data)
pp(formatted_data)

This would output ‘b’ with visual cues showing it exceeds the threshold. This approach separates data manipulation from the formatting provided by PrettyPrint.

Handling Complex Data

PrettyPrint handles deeply nested data structures recursively. However, extremely large or complex data structures might lead to very long output. For such scenarios, consider these strategies:

Integrating with other libraries

PrettyPrint is designed to be compatible with other Python libraries. It works well alongside logging libraries (like logging) where its formatted output can enhance the clarity of log messages. You can seamlessly integrate PrettyPrint into your existing workflows by simply calling pp() on data structures generated or processed by other libraries.

Example with logging:

import logging
from prettyprint import pp

logging.basicConfig(level=logging.INFO)
data = {'message': 'This is a log message', 'details': {'code': 200, 'timestamp': '2024-10-27'}}
logging.info(f"Event details: {pp(data)}")

Performance Optimization

For large datasets, the performance of PrettyPrint might become noticeable. The pp() function generally has a reasonable performance, however for datasets exceeding millions of elements optimization is important. Consider these techniques for improved performance:

API Reference

PrettyPrint Function

The core functionality of PrettyPrint is encapsulated within the pp() function.

Signature:

pp(object, stream=None, indent=4, width=80, depth=None)

Parameters:

Return Value:

The pp() function doesn’t directly return a value; it prints the formatted output to the specified stream. If you need to capture the formatted string instead of printing, redirect the output using the io.StringIO() object:

import io
from prettyprint import pp

data = {'a': 1, 'b': 2}
output_buffer = io.StringIO()
pp(data, stream=output_buffer)
formatted_string = output_buffer.getvalue()
print(formatted_string) #Now formatted_string contains the output

Options Object

Currently, PrettyPrint does not utilize a dedicated “Options Object” for configuration. The parameters directly passed to the pp() function control the formatting behavior. This might change in future versions to provide more fine-grained control and a more object-oriented approach.

Utility Functions

Currently, PrettyPrint does not provide any additional utility functions beyond the core pp() function. Future versions might include helper functions to support additional formatting options or advanced features.

Troubleshooting

Common Errors

Debugging Tips

Frequently Asked Questions

with open("output.txt", "w") as f:
    pp(my_data, stream=f)

Examples

Basic Examples

Example 1: Formatting a dictionary:

from prettyprint import pp

data = {'name': 'John Doe', 'age': 30, 'city': 'New York'}
pp(data)

Output:

{
    'name': 'John Doe',
    'age': 30,
    'city': 'New York'
}

Example 2: Formatting a list:

from prettyprint import pp

data = [1, 2, 3, 4, 5]
pp(data)

Output:

[
    1,
    2,
    3,
    4,
    5
]

Example 3: Formatting a nested structure:

from prettyprint import pp

data = {
    'name': 'Jane Doe',
    'address': {
        'street': '123 Main St',
        'city': 'Anytown',
        'zip': '12345'
    }
}
pp(data)

Output:

{
    'name': 'Jane Doe',
    'address': {
        'street': '123 Main St',
        'city': 'Anytown',
        'zip': '12345'
    }
}

Advanced Examples

Example 1: Handling a large list: To prevent excessively long output, limit the depth of displayed elements.

from prettyprint import pp
import random

large_list = [random.randint(1, 100) for _ in range(1000)]
pp(large_list, depth=10) # Show only the first 10 elements

Example 2: Custom object formatting:

from prettyprint import pp

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return f"Person(name='{self.name}', age={self.age})"

person = Person("Alice", 25)
pp(person)

Output:

Person(name='Alice', age=25)

Example 3: Conditional formatting (pre-processing):

from prettyprint import pp

data = {'a': 10, 'b': 20, 'c': 5}

def highlight_above_threshold(data, threshold=15):
    for key, value in data.items():
        if value > threshold:
            data[key] = f"**{value}**"
    return data

formatted_data = highlight_above_threshold(data)
pp(formatted_data)

Output:

{
    'a': 10,
    'b': '**20**',
    'c': 5
}

Real-world Use Cases