Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] When running inspect on classes that have relationships and other objects referenced in properties, it creates the object. #3031

Closed
2 tasks done
HorselessName opened this issue Jul 11, 2023 · 3 comments

Comments

@HorselessName
Copy link

The bug

I have a class that represents a RemoteClient, and this RemoteClient will have multiple connections, like this:

import paramiko
from rich import inspect  # https://rich.readthedocs.io/en/stable/reference/init.html#rich.inspect


class RemoteClient:
    """This class represents the client to Interact with using SSH.
    It has attributes related to the remote host being connected and the state of the SSH connection.

    Sintax:  host = RemoteClient("192.168.1.1", 'administrator', 'SuperDuperP4ss#23')
    """

    def __init__(self, remote_host: str, login: str, password: str):
        # Init my objet with the att above.
        self.remote_host = remote_host
        self.login = login
        self.password = password

        # My host will have one object of a "Paramiko SSHClient" type
        self.ssh_client = None  # Fix Variable Before Assignment Warning.

    def __str__(self):
        return f"Host => {self.remote_host} \nSessão SSH => {self.ssh_client}"

    @property
    def conexaossh(self):
        try:
            # Try to connect and handle errors - Instantiating an object of the class, not a local object.
            self.ssh_client = paramiko.SSHClient()  # Self instantiates the object, not local.

            # Known_host Policy
            self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

            # Try for 1 second.
            self.ssh_client.connect(self.remote_host, username=self.login, password=self.password, timeout=1)

            return f"Sessão do SSH adicionada com sucesso ao Host. \n{self}"
        except paramiko.ssh_exception.SSHException as ssh_exception:
            return f"SSHException - Failed to connect to the host: {ssh_exception}"
        except TimeoutError as timeout_error:
            return f"TimeoutError - Host unavailable: {timeout_error}"

    # Check if SSH Connection is open
    def isconnected(self):
        """This method will return False if it is not connected, True otherwise."""
        # "Client" method get_transport() returns object "Transport".
        # Ref. https://docs.paramiko.org/en/stable/api/client.html
        if self.ssh_client is None or self.ssh_client.get_transport() is None:
            return False

        if self.ssh_client.get_transport().is_active():
            # The method is available in Transport docs => https://docs.paramiko.org/en/stable/api/transport.html
            # Double check for False Positives from is_active() method
            try:
                # Get Transport object status.
                transport = self.ssh_client.get_transport()
                transport.send_ignore()
                return True
            except EOFError as e:
                # If it hits here, there's been a false positive.
                return False

    def disconnect(self):
        # Use the object attribute that represents the connection state to disconnect.
        if self.ssh_client is not None:
            self.ssh_client.close()
            return "Disconnected from SSH!"
        else:
            return "No active SSH connection."

    def to_dict(self):
        ssh_info = "No active SSH connection"
        if self.isconnected():
            ssh_info = "SSH session is active"

        return {
            'remote_host': self.remote_host,
            'login': self.login,
            'ssh_connection': ssh_info,
            # Outros atributos relevantes...
        }

    def inspecthost(self):
        return inspect(self)

    def inspectsshconnectio(self):
        if self.isconnected():
            return inspect(self.ssh_client)
        return "No SSH Connection at the moment."

At the moment, it has only SSH. I will implement some other protocols using Socket at some other time

So... When commenting out the @property method conexaossh, which establish a SSH connection and creates the relationship in the object RemoteClient, this "BUG" does not occur.

This is the output of the inspect method being called out on the host while this method is commented out:

host = RemoteClient("192.168.1.1", 'administrator', 'SuperDuperP4ss#23')
inspect(host)

┌────────────────────── <class '__main__.RemoteClient'> ──────────────────────┐
│ This class represents the client to Interact with using SSH.                │
│ It has attributes related to the remote host being connected and the state  │
│ of the SSH connection.                                                      │
│                                                                             │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ <__main__.RemoteClient object at 0x000002687D680790>                    │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│                                                                             │
│       login = 'administrator'                                               │
│    password = 'SuperDuperP4ss#23'                                            │
│ remote_host = '192.168.1.1'                                                │
│  ssh_client = None                                                          │
└─────────────────────────────────────────────────────────────────────────────┘

And this is the output when the method that has the referenced object is called, even tho, I did not still create it - It is being created by the inspect method of Rich library:

host = RemoteClient("192.168.1.1", 'administrator', 'SuperDuperP4ss#23')
inspect(host)
┌────────────────────── <class '__main__.RemoteClient'> ──────────────────────┐
│ This class represents the client to Interact with using SSH.                │
│ It has attributes related to the remote host being connected and the state  │
│ of the SSH connection.                                                      │
│                                                                             │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ <__main__.RemoteClient object at 0x0000022ACDDF0810>                    │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│                                                                             │
│  conexaossh = 'Sessão do SSH adicionada com sucesso ao Host. \nHost =>      │
│               172.16.107.9 \nSessão SSH => <paramiko.client.SSHClient       │
│               object at 0x0000022ACDFC4DD0>'                                │
│       login = 'administrator'                                               │
│    password = 'SuperDuperP4ss#23'                                            │
│ remote_host = '192.168.1.1'                                                │
│  ssh_client = <paramiko.client.SSHClient object at 0x0000022ACDFC4DD0>      │
└─────────────────────────────────────────────────────────────────────────────┘

So... I believe this is not suposed to happen, since we're just inspecting stuff.
No need to execute methods when we inspect them... Right?

Also, I did a little reverse engineering on the Inspect method of Rich, but I did not understand why is this happening.
Any help is appreciated it.

Thank you!

@github-actions
Copy link

Thank you for your issue. Give us a little time to review it.

PS. You might want to check the FAQ if you haven't done so already.

This is an automated reply, generated by FAQtory

@willmcgugan
Copy link
Collaborator

Rich can't know that getting a property will execute code you don't want it to execute. You generally would want a property to appear when inspecting it.

Having a property with side-effects, especially one that might connect to the network, is generally not a good idea. Consider making it a regular method.

Copy link

I hope we solved your problem.

If you like using Rich, you might also enjoy Textual

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants