objectcrawler
Basic and lightweight python tool for inspecting python objects.
Originally built for objects defining __slots__, however it also handles the __dict__ attribute just fine.
Installation
- Create a fork of this repo
- Clone the forked repo to your machine
- Install with
pip install ObjectCrawler
For development you can install using the pip -e editable flag.
Feel free to file a pull request if you make any changes!
Usage
Inspecting an object is simple, import the Crawler class and feed it the object in question:
from objectcrawler import Crawler
print(Crawler(...))
Demo
Lets demonstrate this with a simple class:
class Food:
__slots__ = ["name"]
def __init__(self, name: str):
self.name = name
def __repr__(self):
return f"Food({self.name})"
After creating an instance of this class, we can inspect it:
from objectcrawler import Crawler
a = Food("Apple")
print(Crawler(a))
This will output the following table:
────────────┬──────────────┬────────────┬─────────
assignment │ value │ classname │ source
────────────┼──────────────┼────────────┼─────────
~ │ Food(Apple) │ Food │ self
└─ name │ Apple │ str │ Food
Inheritance
The purpose of the source column is to display information about inheritance.
If we create a subclass, we can see this behaviour:
class PreparedFood(Food):
__slots__ = ["prep_time"]
def __init__(self, name: str, prep_time: int):
super().__init__(name)
self.prep_time = prep_time
def __repr__(self):
return f"PreparedFood({self.name}, {self.prep_time})"
b = PreparedFood("Pasta", 10)
print(Crawler(b))
Giving the following table. Note the source column:
──────────────┬──────────────────────────┬───────────────┬───────────────
assignment │ value │ classname │ source
──────────────┼──────────────────────────┼───────────────┼───────────────
~ │ PreparedFood(Pasta, 10) │ PreparedFood │ None
├─ prep_time │ 10 │ int │ PreparedFood
└─ name │ Pasta │ str │ Food
Iterators
Iterators are a special case, since they are implicit storage containers, an attempt is made to "unpack" them into the data tree.
lists, tuples, etc. wil have their assignment set to the index
dicts, OrderedDicts, etc. will have their assignment set to the key (the object must provide a iter() method for this functionality)
class Meal:
__slots__ = ["name", "ingredients"]
def __init__(self, name: str, ingredients: list):
self.name = name
self.ingredients = ingredients
ingredients = [
Food("Cheese"),
PreparedFood("Beans", 10),
PreparedFood("Toast", 5)
]
c = Meal("Cheesy Beans on Toast", ingredients)
print(Crawler(c))
────────────────────┬───────────────────────────────────────────┬───────────────┬───────────────
assignment │ value │ classname │ source
────────────────────┼───────────────────────────────────────────┼───────────────┼───────────────
~ │ <__main__.Meal object at 0x762f4d65de10> │ Meal │ None
├─ name │ Cheesy Beans on Toast │ str │ Meal
└─ ingredients │ iterable: list │ list │ Meal
│ ├─ 0 │ Food(Cheese) │ Food │ Meal
│ │ └─ name │ Cheese │ str │ Food
│ ├─ 1 │ PreparedFood(Beans, 10) │ PreparedFood │ Meal
│ │ ├─ prep_time │ 10 │ int │ PreparedFood
│ │ └─ name │ Beans │ str │ Food
│ └─ 2 │ PreparedFood(Toast, 5) │ PreparedFood │ Meal
│ │ ├─ prep_time │ 5 │ int │ PreparedFood
│ │ └─ name │ Toast │ str │ Food
Differences
If you're trying to debug a class and have one working example of it, you can quickly find the issues by differencing it with a broken version. To do this, you should create two Crawler instances (one working, and one not). You can then "subtract" these objects to reveal the differences.
a = Object(...)
b = Object(...)
crawl_a = Crawler(a)
crawl_b = Crawler(b)
print(crawl_a - crawl_b)
This will print out two joined tables with the differences highlighted in red.
Debug
If you don't trust the output there exists a debug mode for the print which can help you figure out what's going on.
To activate this we should create the actual Crawler object and store it in a variable:
crawl = Crawler(c)
We can then print the info using the print() method. This can take extra args, including debug
crawl.print(debug=True)
────────────────────┬───────────────────────────────────────────┬───────────────┬───────────────┬───────────────────┬───────────────────┬───────────
assignment │ value │ classname │ source │ entity │ parent │ nchildren
────────────────────┼───────────────────────────────────────────┼───────────────┼───────────────┼───────────────────┼───────────────────┼───────────
~ │ <__main__.Meal object at 0x762f4d65de10> │ Meal │ None │ Entity #47004730 │ None │ 2
├─ name │ Cheesy Beans on Toast │ str │ Meal │ Entity #91735648 │ Entity #47004730 │ 0
└─ ingredients │ iterable: list │ list │ Meal │ Entity #43691166 │ Entity #47004730 │ 3
│ ├─ 0 │ Food(Cheese) │ Food │ Meal │ Entity #27979510 │ Entity #43691166 │ 1
│ │ └─ name │ Cheese │ str │ Food │ Entity #27472819 │ Entity #27979510 │ 0
│ ├─ 1 │ PreparedFood(Beans, 10) │ PreparedFood │ Meal │ Entity #62084209 │ Entity #43691166 │ 2
│ │ ├─ prep_time │ 10 │ int │ PreparedFood │ Entity #04848920 │ Entity #62084209 │ 0
│ │ └─ name │ Beans │ str │ Food │ Entity #13535757 │ Entity #62084209 │ 0
│ └─ 2 │ PreparedFood(Toast, 5) │ PreparedFood │ Meal │ Entity #55272230 │ Entity #43691166 │ 2
│ │ ├─ prep_time │ 5 │ int │ PreparedFood │ Entity #32701778 │ Entity #55272230 │ 0
│ │ └─ name │ Toast │ str │ Food │ Entity #67167938 │ Entity #55272230 │ 0
Debug output
To understand what we're seeing here it can be helpful to know what's going on inside this table.
Each row is represented by an Entity object. This stores some information about each attribute of the original object, but most importantly it stores the hierarchy of children.
The extra columns added expose this information.
The entity column contains part of the hash for the Entity in that row.
The parent column contains part of the hash for the Entity that provided the Entity in that row.
The nchildren column is a counter of how many children that entity has, and is used for tree generation.
Formatting
Similar to the debug, print() can also take some basic formatting arguments, whitespace and branch_len.
whitespace dictates the amount of padding added to the end of each column, whereas branch_len controls the length of each "branch" in the tree.
The best way to understand these args is to demonstrate them:
whitespace
crawl.print(whitespace=10)
────────────────────────────┬───────────────────────────────────────────────────┬───────────────────────┬───────────────────────
assignment │ value │ classname │ source
────────────────────────────┼───────────────────────────────────────────────────┼───────────────────────┼───────────────────────
~ │ <__main__.Meal object at 0x762f4d65de10> │ Meal │ None
├─ name │ Cheesy Beans on Toast │ str │ Meal
└─ ingredients │ iterable: list │ list │ Meal
│ ├─ 0 │ Food(Cheese) │ Food │ Meal
│ │ └─ name │ Cheese │ str │ Food
│ ├─ 1 │ PreparedFood(Beans, 10) │ PreparedFood │ Meal
│ │ ├─ prep_time │ 10 │ int │ PreparedFood
│ │ └─ name │ Beans │ str │ Food
│ └─ 2 │ PreparedFood(Toast, 5) │ PreparedFood │ Meal
│ │ ├─ prep_time │ 5 │ int │ PreparedFood
│ │ └─ name │ Toast │ str │ Food
branch_len
crawl.print(branch_len=4)
─────────────────────────────┬───────────────────────────────────────────┬───────────────┬───────────────
assignment │ value │ classname │ source
─────────────────────────────┼───────────────────────────────────────────┼───────────────┼───────────────
~ │ <__main__.Meal object at 0x762f4d65de10> │ Meal │ None
├──── name │ Cheesy Beans on Toast │ str │ Meal
└──── ingredients │ iterable: list │ list │ Meal
│ ├──── 0 │ Food(Cheese) │ Food │ Meal
│ │ └──── name │ Cheese │ str │ Food
│ ├──── 1 │ PreparedFood(Beans, 10) │ PreparedFood │ Meal
│ │ ├──── prep_time │ 10 │ int │ PreparedFood
│ │ └──── name │ Beans │ str │ Food
│ └──── 2 │ PreparedFood(Toast, 5) │ PreparedFood │ Meal
│ │ ├──── prep_time │ 5 │ int │ PreparedFood
│ │ └──── name │ Toast │ str │ Food
Metadata
Release files for objectcrawler 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| objectcrawler-0.0.2.tar.gz | 12.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| objectcrawler-0.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.5 kB
Release files / objectcrawler-0.0.2.tar.gz
| Download URL | objectcrawler-0.0.2.tar.gz |
|---|---|
| Size | 12.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c28b50ed3e13a029dfb0331d6541afc9bf1e7f8ea6eaf5f77a045c50ee0b6100
|
|
BLAKE2b-256 checksum How to use checksums |
74eaad13d4bf7bf00e97aa7bc7a5d1004af48b72122a44c772e5af620f221f12
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/5.0.0 CPython/3.9.19
|
Release files / objectcrawler-0.0.2-py3-none-any.whl
| Download URL | objectcrawler-0.0.2-py3-none-any.whl |
|---|---|
| Size | 10.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ab2cdc7a86e976f3c95d227b8254eaf3277d963641671ee78425204bade609d3
|
|
BLAKE2b-256 checksum How to use checksums |
bf3da199d767f7faa61ab73bd332bb358f663c647a45b89abf6ccf6a0166ffc6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/5.0.0 CPython/3.9.19
|
Security for objectcrawler 0.0.2
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.