Pythonistas follow an implicit convention to create special __repr__ methods that return text closely resembling the code used to construct the object. With this library, you can easily implement __repr__ for your own classes.
Table of contents
Installation
You can install printo with pip:
pip install printo
You can also use instld to quickly try this package and others without installing them.
Basic usage
The main function in this library is describe_call; it returns a string representing the initialization code for your object. There are three required positional parameters:
- A class name string or a class object.
- A
listortupleof positional arguments. - A
dictof keyword arguments, where the keys are the names of the arguments, and the values are arbitrary objects.
Here's a simple example of how it works:
from printo import describe_call
print(
describe_call(
'MyClassName',
(1, 2, 'some text'),
{'variable_name': 1, 'second_variable_name': 'kek'},
)
)
#> MyClassName(1, 2, 'some text', variable_name=1, second_variable_name='kek')
Filtering
You can prevent individual parameters from being displayed. To do this, pass a dict to the filters parameter. The keys identify arguments by index or name. The values are functions that return a bool — True keeps the argument and False skips it:
print(
describe_call(
'MyClassName',
(1, 2, 'some text'),
{'variable_name': 1, 'second_variable_name': 'kek'},
filters={1: lambda x: False if x == 2 else True, 'second_variable_name': lambda x: False},
)
)
#> MyClassName(1, 'some text', variable_name=1)
You can also use the provided not_none filter to automatically exclude None values:
from printo import not_none
print(
describe_call(
'MyClassName',
(1, None),
{},
filters={1: not_none},
)
)
#> MyClassName(1)
Custom display of objects
By default, all argument values are represented in the same way as the standard repr function would show them. There are only three exceptions:
- For regular functions, the function name is displayed.
- For classes, the class name is displayed.
- For lambda functions, the complete source code is displayed. However, if a single line of source code contains more than one lambda function, only the
λsymbol is displayed (this is a technical limitation of source code reflection in Python).
You can provide a custom serialization function for each argument value via the serializer parameter:
print(
describe_call(
'MyClassName',
(1, 2, 'lol'),
{'variable_name': 1, 'second_variable_name': 'kek'},
serializer=lambda x: repr(x * 2),
)
)
#> MyClassName(2, 4, 'lollol', variable_name=2, second_variable_name='kekkek')
The default serializer is superrepr, and you can also import and use it directly to display individual values:
import functools
from printo import superrepr
def my_function():
pass
class MyClass:
def my_method(self):
pass
print(superrepr(my_function))
#> my_function
print(superrepr(MyClass))
#> MyClass
print(superrepr(MyClass().my_method))
#> my_method
print(superrepr(functools.partial(my_function)))
#> functools.partial(my_function)
Placeholders
For individual parameters, you can pass arbitrary strings that will be displayed instead of the actual values. This can be useful, for example, to hide the values of sensitive fields when serializing objects.
Pass a dict to the placeholders parameter, where the keys are argument names (for keyword arguments) or indices (for positional parameters, zero-indexed), and the values are strings:
print(
describe_call(
'MySuperClass',
(1, 2, 'lol'),
{'variable_name': 1, 'second_variable_name': 'kek'},
placeholders={
1: '***',
'variable_name': '***',
},
)
)
#> MySuperClass(1, ***, 'lol', variable_name=***, second_variable_name='kek')
🤓 If you set a placeholder for a parameter, the custom serializer will not be applied to it.
Output limits
You can limit the length of individual serialized values with item_limit, and the total length of the output string with total_limit. Both are disabled by default (None).
item_limit truncates each serialized value to at most N characters, appending ... if the value is longer:
print(
describe_call(
'MyClass',
(123456789,),
{'name': 'a very long string'},
item_limit=5,
)
)
#> MyClass(12345..., name='a ver'...)
total_limit limits the total length of the output. If the output would be too long, whole items are dropped from the right and replaced with ...:
print(
describe_call(
'MyClass',
(),
{'a': 1, 'b': 2, 'c': 3},
total_limit=15,
)
)
#> MyClass(a=1, ...)
If total_limit is too small to fit even ClassName(), a ValueError is raised. The minimum valid value is the length of the resolved class name plus 2.
Auto mode
⚠️ Auto mode is currently experimental, so there may be some bugs.
You can remove the boilerplate code by using the @repred decorator for your class:
from printo import repred
@repred
class SomeClass:
def __init__(self, a, b, c, *args, **kwargs):
self.a = a
self.b = b
self.c = c
self.args = args
self.kwargs = kwargs
print(SomeClass(1, 2, 3))
#> SomeClass(1, 2, 3)
print(SomeClass(1, 2, 3, 4, 5))
#> SomeClass(1, 2, 3, 4, 5)
print(SomeClass(1, 2, 3, 4, 5, d=lambda x: x))
#> SomeClass(1, 2, 3, 4, 5, d=lambda x: x)
How does it work? Behind the scenes, the decorator uses AST analysis to generate code. The decorator attempts to determine which arguments passed to __init__ are stored in which attributes. In other words, it looks for direct assignments of the form self.a = a in the __init__ method.
Conditional (ternary) assignments are also recognized. If you write self.a = a if a else default, the decorator understands that parameter a is stored in attribute a:
@repred
class SomeClass:
def __init__(self, a, b):
self.a = a if a is not None else 0
self.b = b
print(SomeClass(42, 'hello'))
#> SomeClass(a=42, b='hello')
If there is no direct assignment of a specific argument, an exception will be raised:
@repred
class SomeClass:
def __init__(self, a):
...
#> ...
#> printo.errors.ParameterMappingNotFoundError: No internal object property or custom getter was found for the parameter a.
↑ The error occurs when the class is decorated.
If, for some reason, you are unable to specify this mapping in the body of the __init__ method, you can pass a function for a specific parameter that will extract it:
@repred(getters={'a': lambda x: x.a})
class SomeClass:
def __init__(self, a):
self.a = self.convert_a(a)
def convert_a(self, a):
return a
print(SomeClass(123))
#> SomeClass(a=123)
By default, @repred displays all arguments as keywords in most cases. However, you can pass the prefer_positional argument to the decorator, which will cause it to prefer omitting argument names in such cases:
@repred
class Class1:
def __init__(self, a, b):
self.a = a
self.b = b
@repred(prefer_positional=True)
class Class2:
def __init__(self, a, b):
self.a = a
self.b = b
print(Class1(123, 456))
#> Class1(a=123, b=456)
print(Class2(123, 456))
#> Class2(123, 456)
You can also choose to display only certain parameters as positional:
@repred(positionals=['a'])
class SomeClass:
def __init__(self, a, b):
self.a = a
self.b = b
print(SomeClass(123, 456))
#> SomeClass(123, b=456)
If you want to prevent certain __init__ parameters from being displayed, you can add their names to the ignore list:
@repred(ignore=['a'])
class SomeClass:
def __init__(self, a, b):
self.a = a
self.b = b
print(SomeClass(123, 456))
#> SomeClass(b=456)
You can also add value-based filters for individual arguments by passing a dict of filters, similar to how it works in manual mode:
from printo import not_none
@repred(filters={'a': not_none})
class SomeClass:
def __init__(self, a, b):
self.a = a
self.b = b
print(SomeClass(None, None))
#> SomeClass(b=None)
print(SomeClass(123, 456))
#> SomeClass(a=123, b=456)
By default, the class name is displayed based on its __name__ attribute, but you can configure it to use the __qualname__ attribute instead:
def function():
@repred(qualname=True)
class SomeClass:
def __init__(self, a, b):
self.a = a
self.b = b
return SomeClass
print(function()(123, 456))
#> function.<locals>.SomeClass(a=123, b=456)
Metadata
Release files for printo 0.0.29
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| printo-0.0.29.tar.gz | 14.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| printo-0.0.29-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.1 kB
Release files / printo-0.0.29.tar.gz
| Download URL | printo-0.0.29.tar.gz |
|---|---|
| Size | 14.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1bb753eb0ebc0047d70022ba61f9570ce04cd54297bf5328b95f7cdeb3772660
|
|
BLAKE2b-256 checksum How to use checksums |
a39b93bbcb7b7d5634000a71822a5054a7e3e931cba8a09e0f2940c7d265dca3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 1, 2026.
Transparency logRelease files / printo-0.0.29-py3-none-any.whl
| Download URL | printo-0.0.29-py3-none-any.whl |
|---|---|
| Size | 12.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2cce3634446806ea063adc38ff16855b58c653bd589b33eea9d30cef07a131c4
|
|
BLAKE2b-256 checksum How to use checksums |
d1759aae77ad6b5cf075e34de31cb78d6db9319ee2c2d32b21cd5ecfa5d8a1dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 1, 2026.
Transparency log