I am learning about Dependency Injection and I have been recently implementing the following classes for an app that executes commands over ssh using Python. I am confused about whether I am using it in the wrong way.
I am using a
ConnectionFactory class to create an
ssh object that I call dynamic, because it depends on the user input (username, password).
Below is a working simplified example that uses
My question is whether it's a code smell, the fact that I am instantiating manually
RemoteBashCommandRunner along with its dependencies.
from injector import Injector, inject from paramiko import SSHClient, AutoAddPolicy class A: pass class B: pass class C: pass class D: pass HOST, USERNAME, PASSWORD = "localhost", "username", "password" class ConnectionFactory: @inject def __init__(self, a: A, b: B): self.a, self.b = a, b def get_connection(self, host): # in a real implementation, this method calls the dependencies set in constructor ssh = SSHClient() ssh.set_missing_host_key_policy(AutoAddPolicy()) ssh.connect(host, 22, USERNAME, PASSWORD) return ssh class RemoteBashCommandRunner: def __init__(self, ssh: SSHClient, c: C, d: D) -> None: self.ssh = ssh self.c, self.d = c, d def run(self, command: str): _, stdout, _ = self.ssh.exec_command(command) print(stdout.read()) def main(): container = Injector() connection_factory = container.get(ConnectionFactory) ssh = connection_factory.get_connection(HOST) runner = RemoteBashCommandRunner(ssh, C(), D()) runner.run("echo hello") main()
I have been also thinking about binding the
ssh object in the container and getting an instance of
RemoteBashCommandRunner by using the
... class RemoteBashCommandRunner: @inject def __init__(self, ssh: SSHClient, c: C, d: D) -> None: ... container.binder.bind(SSHClient, to=lambda: ssh) container.get(RemoteBashCommandRunner).run("echo hello")
Or maybe just remove the
ssh object from the constructor, and use it as an argument in the method.
class RemoteBashCommandRunner: @inject def __init__(self, c: C, d: D) -> None: def run(self, ssh, command: str): # no more manual binding container.get(RemoteBashCommandRunner).run(ssh, "echo hello")
Which one of the 3 approaches is better?