Skip to main content

simple object crawling debug tool

Project description

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

  1. Create a fork of this repo
  2. Clone the forked repo to your machine
  3. 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


Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

objectcrawler-0.0.2.tar.gz (12.5 kB view details)

Uploaded Source

Built Distribution

objectcrawler-0.0.2-py3-none-any.whl (10.1 kB view details)

Uploaded Python 3

File details

Details for the file objectcrawler-0.0.2.tar.gz.

File metadata

  • Download URL: objectcrawler-0.0.2.tar.gz
  • Upload date:
  • Size: 12.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.0.0 CPython/3.9.19

File hashes

Hashes for objectcrawler-0.0.2.tar.gz
Algorithm Hash digest
SHA256 c28b50ed3e13a029dfb0331d6541afc9bf1e7f8ea6eaf5f77a045c50ee0b6100
MD5 8e9c818a04aab82fd54199f266c65520
BLAKE2b-256 74eaad13d4bf7bf00e97aa7bc7a5d1004af48b72122a44c772e5af620f221f12

See more details on using hashes here.

File details

Details for the file objectcrawler-0.0.2-py3-none-any.whl.

File metadata

File hashes

Hashes for objectcrawler-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 ab2cdc7a86e976f3c95d227b8254eaf3277d963641671ee78425204bade609d3
MD5 5ba18163a4f081eb45ae18814699acc3
BLAKE2b-256 bf3da199d767f7faa61ab73bd332bb358f663c647a45b89abf6ccf6a0166ffc6

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page