Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
This library provides short, fast, configurable string representations, and an easy API for registering your own. It's an improvement of the standard library module reprlib
(repr
in Python 2).
Just use the cheap_repr
function instead of repr
:
>>> from cheap_repr import cheap_repr
>>> cheap_repr(list(range(100)))
'[0, 1, 2, ..., 97, 98, 99]'
cheap_repr
knows how to handle many different types out of the box. You can register a function for any type, and pull requests to make these part of the library are welcome. If it doesn't know how to handle a particular type, the default repr()
is used, possibly truncated:
>>> class MyClass(object):
... def __init__(self, items):
... self.items = items
...
... def __repr__(self):
... return 'MyClass(%r)' % self.items
...
>>> c = MyClass(list(range(20)))
>>> c
MyClass([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
>>> cheap_repr(c)
'MyClass([0, 1, 2, 3, 4, 5, 6... 13, 14, 15, 16, 17, 18, 19])'
cheap_repr
is meant to prevent slow, expensive computations of string representations. So if it finds that a particular class can potentially produce very long representations, the class will be suppressed, meaning that in future the __repr__
won't be calculated at all:
>>> cheap_repr(MyClass(list(range(1000))))
'MyClass([0, 1, 2, 3, 4, 5, 6...94, 995, 996, 997, 998, 999])'
.../cheap_repr/__init__.py:80: ReprSuppressedWarning: MyClass.__repr__ is too long and has been suppressed. Register a repr for the class to avoid this warning and see an informative repr again, or increase cheap_repr.suppression_threshold
>>> cheap_repr(MyClass(list(range(1000))))
'<MyClass instance at 0x1034de7f0 (repr suppressed)>'
cheap_repr.suppression_threshold
refers to the attribute on the function itself, not the module. By default it's 300, meaning that a repr
longer than 300 characters will trigger the suppression.
For example:
>>> from cheap_repr import register_repr
>>> @register_repr(MyClass)
... def repr_my_class(x, helper):
... return helper.repr_iterable(x.items, 'MyClass([', '])')
...
>>> cheap_repr(MyClass(list(range(1000))))
'MyClass([0, 1, 2, 3, 4, 5, ...])'
In general, write a function that takes two arguments (x, helper)
and decorate it with register_repr(cls)
. Then cheap_repr(x)
where isinstance(x, cls)
will dispatch to that function, unless there is also a registered function for a subclass which x
is also an instance of. More precisely, the function corresponding to the first class in the MRO will be used. This is in contrast to the standard library module reprlib
, which cannot handle subclasses that aren't explicitly 'registered', or classes with the same name.
The helper
argument is an object with a couple of useful attributes and methods:
repr_iterable(iterable, left, right, end=False, length=None)
produces a comma-separated representation of iterable
, automatically handling nesting and iterables that are too long, surrounded by left
and right
. The number of items is limited to func.maxparts
(see the configuration section below).
Set end=True
to include items from both the beginning and end, possibly leaving out items
in the middle. Only do this if iterable
supports efficient slicing at the end, e.g. iterable[-3:]
.
Provide the length
parameter if len(iterable)
doesn't work. Usually this is not needed.
truncate(string)
returns a version of string
at most func.maxparts
characters long, with the middle replaced by ...
if necessary.
level
indicates how much nesting is still allowed in the result. If it's 0, return something minimal such as [...]
to indicate that the original object is too deep to show all its contents. Otherwise, if you use cheap_repr
on several items inside x
, pass helper.level - 1
as the second argument, e.g. ', '.join(cheap_repr(item, helper.level - 1) for item in x)
.
If an exception occurs in cheap_repr
, whether from a registered repr function or the usual __repr__
, the exception will be caught and the cheap repr of the class will be suppressed:
>>> @register_repr(MyClass)
... def repr_my_class(x, helper):
... return 'MyClass([%r, ...])' % x.items[0]
...
>>> cheap_repr(MyClass([]))
'<MyClass instance at 0x10f44ec50 (exception in repr)>'
.../cheap_repr/__init__.py:123: ReprSuppressedWarning: Exception 'IndexError: list index out of range' in repr_my_class for object of type MyClass. The repr has been suppressed for this type.
...
>>> cheap_repr(MyClass([1, 2, 3]))
'<MyClass instance at 0x10f44ecc0 (repr suppressed)>'
If you would prefer exceptions to bubble up normally, you can:
cheap_repr.raise_exceptions = True
to globally make all exceptions bubble up.__repr__
of a class, call raise_exceptions_from_default_repr()
.repr_my_class.raise_exceptions = True
(substituting your own registered repr function) to make exceptions bubble from that function. The way to find the relevant function is in the next section.To configure a specific function, you set attributes on that function. To find the function corresponding to a class, use find_repr_function
:
>>> from cheap_repr import find_repr_function
>>> find_repr_function(MyClass)
<function repr_my_class at 0x10f43d8c8>
For most functions, there are two attributes available to configure, but contributors and library writers are encouraged to add arbitrary attributes for their own functions. The first attribute is raise_exceptions
, described in the previous section.
The other configurable attribute is maxparts
. All registered repr functions have this attribute. It determines the maximum number of 'parts' (e.g. list elements or string characters, the meaning depends on the function) from the input that the output can display without truncation. The default value is 6. The decorator @maxparts(n)
conveniently sets the attribute to make writing your own registered functions nicer. For example:
>>> from cheap_repr import maxparts
>>> @register_repr(MyClass)
... @maxparts(2)
... def repr_my_class(x, helper):
... return helper.repr_iterable(x.items, 'MyClass([', '])')
...
>>> cheap_repr(MyClass([1, 2, 3, 4]))
'MyClass([1, 2, ...])'
>>> find_repr_function(MyClass).maxparts = 3
>>> cheap_repr(MyClass([1, 2, 3, 4]))
'MyClass([1, 2, 3, ...])'
The functions for DataFrame
s and Series
from the pandas
library don't use maxparts
.
For the DataFrame
function there's max_rows
and max_cols
. For the Series
function there's just max_rows
.
cheap_repr
takes an optional argument level
which controls the display of nested objects. Typically this decreases through recursive calls, and when it's 0, the contents of the object aren't shown. See 'Registering your own repr function' for more details. This means you can change the amount of nested data in the output of cheap_repr
by changing the level
argument. The default value is cheap_repr.max_level
, which is initially 3. This means that changing cheap_repr.max_level
will effectively change the level
argument whenever it isn't explicitly specified.
These things that can be configured globally:
cheap_repr.suppression_threshold
, discussed in the 'Suppression of long reprs' section.cheap_repr.raise_exceptions = True
or calling raise_exceptions_from_default_repr()
.cheap_repr.max_level
, discussed above.basic_repr(x)
returns a string that looks like the default object.__repr__
. This is handy if you don't want to write your own repr function to register. Simply register this function instead, e.g.
>>> from cheap_repr import basic_repr
>>> register_repr(MyClass)(basic_repr)
>>> cheap_repr(MyClass([1, 2, 3, 4]))
'<MyClass instance at 0x10f39dda0>'
normal_repr(x)
returns repr(x)
- register it with a class to indicate that its own __repr__
method is already fine. This prevents it from being supressed when its output is a bit long.
try_register_repr
is handy when you want to register a repr function for a class that may not exist, e.g. if the class is in a third party package that may not be installed. See the docstring for more details.
FAQs
Better version of repr/reprlib for short, cheap string representations.
We found that cheap-repr demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.