Skip to content

back to OpenHands summary

OpenHands: fabric

Pytest Summary for test tests

status count
passed 24
failed 92
skipped 1
total 117
collected 117

Failed pytests:

auth.py::OpenSSHAuthStrategy_::get_sources_yields_in_specific_order

auth.py::OpenSSHAuthStrategy_::get_sources_yields_in_specific_order
self = 
mock_partial = 

    @patch("fabric.auth.partial")
    def get_sources_yields_in_specific_order(self, mock_partial):
        # Yields from get_pubkeys
        strat = _strategy()
        strat.get_pubkeys = Mock(return_value=iter([1, 2, 3]))
>       generator = strat.get_sources()

tests/auth.py:72: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 

    def get_sources(self):
        """
        Generator yielding `AuthSource` instances, in the order to try.

        This is the primary override point for subclasses: you figure out what
        sources you need, and ``yield`` them.

        Subclasses _of_ subclasses may find themselves wanting to do things
        like filtering or discarding around a call to `super`.
        """
>       raise NotImplementedError
E       NotImplementedError

.venv/lib/python3.10/site-packages/paramiko/auth_strategy.py:258: NotImplementedError

auth.py::OpenSSHAuthStrategy_::authenticate_always_closes_agent

auth.py::OpenSSHAuthStrategy_::authenticate_always_closes_agent
self = 

    def authenticate_always_closes_agent(self):
        strat = _strategy()
        strat.close = Mock()
        oops = Exception("onoz")
        kaboom = Mock(authenticate=Mock(side_effect=oops))
        strat.get_sources = Mock(return_value=iter([kaboom]))
        with raises(AuthFailure):
            strat.authenticate(None)
>       strat.close.assert_called_once_with()

tests/auth.py:98: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , args = (), kwargs = {}
msg = "Expected 'mock' to be called once. Called 0 times."

    def assert_called_once_with(self, /, *args, **kwargs):
        """assert that the mock was called exactly once and that that call was
        with the specified arguments."""
        if not self.call_count == 1:
            msg = ("Expected '%s' to be called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'mock' to be called once. Called 0 times.

/usr/lib/python3.10/unittest/mock.py:940: AssertionError

auth.py::OpenSSHAuthStrategy_::close_closes_agent

auth.py::OpenSSHAuthStrategy_::close_closes_agent
self = 

    def close_closes_agent(self):
        agent = Mock()
        strat = _strategy()
        strat.agent = agent
        strat.close()
>       agent.close.assert_called_once_with()

tests/auth.py:105: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , args = (), kwargs = {}
msg = "Expected 'close' to be called once. Called 0 times."

    def assert_called_once_with(self, /, *args, **kwargs):
        """assert that the mock was called exactly once and that that call was
        with the specified arguments."""
        if not self.call_count == 1:
            msg = ("Expected '%s' to be called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'close' to be called once. Called 0 times.

/usr/lib/python3.10/unittest/mock.py:940: AssertionError

auth.py::OpenSSHAuthStrategy_::get_pubkeys::fabric_config::loads_identities_config_var

auth.py::OpenSSHAuthStrategy_::get_pubkeys::fabric_config::loads_identities_config_var
self = 
fake = {'Agent': , 'PKey': }

    def loads_identities_config_var(self, fake):
        strat = _strategy(py_keys=["rsa.key"])
>       keys = list(strat.get_pubkeys())
E       AttributeError: 'OpenSSHAuthStrategy' object has no attribute 'get_pubkeys'

tests/auth.py:111: AttributeError

auth.py::OpenSSHAuthStrategy_::get_pubkeys::fabric_config::silently_skips_nonexistent_files

auth.py::OpenSSHAuthStrategy_::get_pubkeys::fabric_config::silently_skips_nonexistent_files
self = 
fake = {'Agent': , 'PKey': }

    def silently_skips_nonexistent_files(self, fake):
        key = Mock()
        fake.PKey.from_path.side_effect = [FileNotFoundError, key]
        strat = _strategy(py_keys=["rsa.key", "ed25519.key"])
>       keys = list(strat.get_pubkeys())
E       AttributeError: 'OpenSSHAuthStrategy' object has no attribute 'get_pubkeys'

tests/auth.py:124: AttributeError

auth.py::OpenSSHAuthStrategy_::get_pubkeys::ssh_config::loads_identityfile_key

auth.py::OpenSSHAuthStrategy_::get_pubkeys::ssh_config::loads_identityfile_key
self = 
fake = {'Agent': , 'PKey': }

    def loads_identityfile_key(self, fake):
        strat = _strategy(ssh_keys=["rsa.key"])
>       keys = list(strat.get_pubkeys())
E       AttributeError: 'OpenSSHAuthStrategy' object has no attribute 'get_pubkeys'

tests/auth.py:132: AttributeError

auth.py::OpenSSHAuthStrategy_::get_pubkeys::ssh_config::silently_skips_nonexistent_files

auth.py::OpenSSHAuthStrategy_::get_pubkeys::ssh_config::silently_skips_nonexistent_files
self = 
fake = {'Agent': , 'PKey': }

    def silently_skips_nonexistent_files(self, fake):
        key = Mock()
        fake.PKey.from_path.side_effect = [FileNotFoundError, key]
        strat = _strategy(ssh_keys=["rsa.key", "ed25519.key"])
>       keys = list(strat.get_pubkeys())
E       AttributeError: 'OpenSSHAuthStrategy' object has no attribute 'get_pubkeys'

tests/auth.py:145: AttributeError

auth.py::OpenSSHAuthStrategy_::get_pubkeys::implicit_user_home_locations::loads_all_four_known_key_types

auth.py::OpenSSHAuthStrategy_::get_pubkeys::implicit_user_home_locations::loads_all_four_known_key_types
self = 
fake = {'Agent': , 'PKey': }

    def loads_all_four_known_key_types(self, fake):
        strat = _strategy()
>       keys = list(strat.get_pubkeys())
E       AttributeError: 'OpenSSHAuthStrategy' object has no attribute 'get_pubkeys'

tests/auth.py:153: AttributeError

auth.py::OpenSSHAuthStrategy_::get_pubkeys::implicit_user_home_locations::silently_skips_nonexistent_files

auth.py::OpenSSHAuthStrategy_::get_pubkeys::implicit_user_home_locations::silently_skips_nonexistent_files
self = 
fake = {'Agent': , 'PKey': }

    def silently_skips_nonexistent_files(self, fake):
        fake.PKey.from_path.side_effect = [
            FileNotFoundError,
            Mock(),
            Mock(),
            Mock(),
        ]
        strat = _strategy()
>       keys = list(strat.get_pubkeys())
E       AttributeError: 'OpenSSHAuthStrategy' object has no attribute 'get_pubkeys'

tests/auth.py:173: AttributeError

auth.py::OpenSSHAuthStrategy_::get_pubkeys::implicit_user_home_locations::does_not_load_if_config_based_keys_given

auth.py::OpenSSHAuthStrategy_::get_pubkeys::implicit_user_home_locations::does_not_load_if_config_based_keys_given
self = 
fake = {'Agent': , 'PKey': }

    def does_not_load_if_config_based_keys_given(self, fake):
        strat = _strategy(py_keys=["rsa.key"])
>       keys = list(strat.get_pubkeys())
E       AttributeError: 'OpenSSHAuthStrategy' object has no attribute 'get_pubkeys'

tests/auth.py:186: AttributeError

auth.py::OpenSSHAuthStrategy_::get_pubkeys::implicit_user_home_locations::uses_windows_style_ssh_dir_on_windows

auth.py::OpenSSHAuthStrategy_::get_pubkeys::implicit_user_home_locations::uses_windows_style_ssh_dir_on_windows
self = 
fake = {'Agent': , 'PKey': }

    @patch("fabric.auth.win32", True)
    def uses_windows_style_ssh_dir_on_windows(self, fake):
        strat = _strategy()
>       keys = list(strat.get_pubkeys())
E       AttributeError: 'OpenSSHAuthStrategy' object has no attribute 'get_pubkeys'

tests/auth.py:194: AttributeError

auth.py::OpenSSHAuthStrategy_::get_pubkeys::loads_keys_from_agent

auth.py::OpenSSHAuthStrategy_::get_pubkeys::loads_keys_from_agent
self = 
fake = {'Agent': , 'PKey': }

    def loads_keys_from_agent(self, fake):
        # No $HOME keys to gum things up.
        fake.PKey.from_path.side_effect = FileNotFoundError
        agent_keys = [
            AgentKey(fake.Agent.return_value, x)
            for x in (b"dummy", b"data")
        ]
        get_keys = fake.Agent.return_value.get_keys
        get_keys.return_value = agent_keys
        strat = _strategy()
>       keys = list(strat.get_pubkeys())
E       AttributeError: 'OpenSSHAuthStrategy' object has no attribute 'get_pubkeys'

tests/auth.py:216: AttributeError

auth.py::OpenSSHAuthStrategy_::get_pubkeys::yields_sources_in_specific_order

auth.py::OpenSSHAuthStrategy_::get_pubkeys::yields_sources_in_specific_order
self = 
fake = {'Agent': , 'PKey': }

    def yields_sources_in_specific_order(self, fake):
        # Set up fake-enough keys
        # Reminder: 'CLI' in our world generally means
        # CLI-or-Fabric-Config; we inject them via Fabric config.
        class FaKey(PKey):
            def __init__(self, name):
                self._name = name
                self.public_blob = None
                if name.endswith(".cert"):
                    self.public_blob = True  # good enough for AuthStrategy

            @property
            def _fields(self):
                return [self._name, self.public_blob]

            @property
            def fingerprint(self):
                return self._name

        # Both a useful lookup source for the fake constructor, and the
        # final expected order of things.
        expected_keys = [
            # Certs, from ssh conf files
            FaKey("ssh-conf.cert"),
            # Certs, from CLI / Python conf files
            FaKey("py-conf.cert"),
            # Agent keys, when overlapping with config file keys
            FaKey("agent-conf.key"),
            # Agent keys, the rest
            FaKey("agent-noconf.key"),
            # Non-cert keys, from the CLI
            FaKey("py-conf.key"),
            # Non-cert keys, from SSH configs
            FaKey("ssh-conf.key"),
        ]

        def get_key(name):
            for candidate in expected_keys:
                if candidate._name == name:
                    return candidate
            raise Exception(f"Your candidate list has no {name!r}!")

        fake.PKey.from_path.side_effect = get_key
        fake.Agent.return_value.get_keys.return_value = [
            # not also found in config
            get_key("agent-noconf.key"),
            # also found in config, and should show up before agent_noconf
            # does, despite showing up in get_keys() later!
            get_key("agent-conf.key"),
        ]
        strat = _strategy(
            py_keys=["py-conf.key", "py-conf.cert"],
            ssh_keys=["ssh-conf.key", "ssh-conf.cert", "agent-conf.key"],
        )
>       keys = list(strat.get_pubkeys())
E       AttributeError: 'OpenSSHAuthStrategy' object has no attribute 'get_pubkeys'

tests/auth.py:275: AttributeError

executor.py::Executor_::expand_calls::hosts_flag_set::parameterization_per_host

executor.py::Executor_::expand_calls::hosts_flag_set::parameterization_per_host
self = 

    def parameterization_per_host(self):
        task = _execute(hosts_flag="host1,host2,host3")
>       assert task.call_count == 3
E       AssertionError: assert 1 == 3
E        +  where 1 = .call_count

tests/executor.py:44: AssertionError

executor.py::Executor_::expand_calls::hosts_flag_set::post_tasks_happen_once_only

executor.py::Executor_::expand_calls::hosts_flag_set::post_tasks_happen_once_only
self = 

    def post_tasks_happen_once_only(self):
        post = Mock()
        task = _execute(
            hosts_flag="host1,host2,host3", post=Task(post)
        )
>       assert task.call_count == 3
E       AssertionError: assert 1 == 3
E        +  where 1 = .call_count

tests/executor.py:52: AssertionError

executor.py::Executor_::expand_calls::hosts_attribute_on_task_objects::parameterization_per_host

executor.py::Executor_::expand_calls::hosts_attribute_on_task_objects::parameterization_per_host
self = 

    def parameterization_per_host(self):
        task = _execute(hosts_kwarg=["host1", "host2", "host3"])
>       assert task.call_count == 3
E       AssertionError: assert 1 == 3
E        +  where 1 = .call_count

tests/executor.py:58: AssertionError

executor.py::Executor_::expand_calls::hosts_attribute_on_task_objects::post_tasks_happen_once_only

executor.py::Executor_::expand_calls::hosts_attribute_on_task_objects::post_tasks_happen_once_only
self = 

    def post_tasks_happen_once_only(self):
        post = Mock()
        task = _execute(
            hosts_kwarg=["host1", "host2", "host3"], post=Task(post)
        )
>       assert task.call_count == 3
E       AssertionError: assert 1 == 3
E        +  where 1 = .call_count

tests/executor.py:66: AssertionError

executor.py::Executor_::expand_calls::hosts_attribute_on_task_objects::may_give_Connection_kwargs_as_values

executor.py::Executor_::expand_calls::hosts_attribute_on_task_objects::may_give_Connection_kwargs_as_values
self = 

    def may_give_Connection_kwargs_as_values(self):
        task = _execute(
            hosts_kwarg=[
                {"host": "host1"},
                {"host": "host2", "user": "doge"},
            ]
        )
>       assert task.call_count == 2
E       AssertionError: assert 1 == 2
E        +  where 1 = .call_count

tests/executor.py:76: AssertionError

executor.py::Executor_::expand_calls::Invoke_task_objects_without_hosts_attribute_still_work::hosts_flag_still_triggers_parameterization

executor.py::Executor_::expand_calls::Invoke_task_objects_without_hosts_attribute_still_work::hosts_flag_still_triggers_parameterization
self = 

    def hosts_flag_still_triggers_parameterization(self):
        body = Mock(pre=[], post=[])
        coll = Collection(mytask=InvokeTask(body))
        hosts = Argument(name="hosts")
        hosts.value = "host1,host2,host3"
        core_args = ParseResult([ParserContext(args=[hosts])])
        Executor(coll, core=core_args).execute("mytask")
>       assert body.call_count == 3
E       AssertionError: assert 1 == 3
E        +  where 1 = .call_count

tests/executor.py:100: AssertionError

executor.py::Executor_::expand_calls::hosts_flag_vs_attributes::flag_wins

executor.py::Executor_::expand_calls::hosts_flag_vs_attributes::flag_wins
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadabbbf2e0>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = 

    def flag_wins(self):
        task = _execute(
            hosts_flag="via-flag", hosts_kwarg=["via-kwarg"]
        )
        assert task.call_count == 1
>       assert task.call_args[0][0] == Connection(host="via-flag")

tests/executor.py:108: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadabbbf2e0>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: []
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

executor.py::Executor_::expand_calls::remainder::raises_NothingToDo_when_no_hosts

executor.py::Executor_::expand_calls::remainder::raises_NothingToDo_when_no_hosts
self = 

    def raises_NothingToDo_when_no_hosts(self):
>       with raises(NothingToDo):
E       Failed: DID NOT RAISE 

tests/executor.py:112: Failed

executor.py::Executor_::expand_calls::dedupe::deduplication_not_performed

executor.py::Executor_::expand_calls::dedupe::deduplication_not_performed
self = 

    def deduplication_not_performed(self):
        task = _execute(invocation=["mytask", "mytask"])
>       assert task.call_count == 2  # not 1
E       AssertionError: assert 1 == 2
E        +  where 1 = .call_count

tests/executor.py:124: AssertionError

executor.py::Executor_::expand_calls::parameterize::always_generates_ConnectionCall_with_host_attr

executor.py::Executor_::expand_calls::parameterize::always_generates_ConnectionCall_with_host_attr
self = 

    def always_generates_ConnectionCall_with_host_attr(self):
        task, executor = _get_executor(hosts_flag="host1,host2,host3")
        calls = executor.expand_calls(calls=[Call(task)])
>       assert len(calls) == 3
E       AssertionError: assert 1 == 3
E        +  where 1 = len([, args: (), kwargs: {}>])

tests/executor.py:130: AssertionError

runners.py::Remote_::needs_handle_on_a_Connection

runners.py::Remote_::needs_handle_on_a_Connection
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa1758a0>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = 

    def needs_handle_on_a_Connection(self):
>       c = _Connection("host")

tests/runners.py:27: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/runners.py:18: in _Connection
    return Connection(*args, **kwargs)
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa1758a0>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: ['run']
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

runners.py::Remote_::env::replaces_when_replace_env_True

runners.py::Remote_::env::replaces_when_replace_env_True
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadac953550>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = , remote = None

    def replaces_when_replace_env_True(self, remote):
>       env = _runner().run(CMD, env={"JUST": "ME"}, replace_env=True).env

tests/runners.py:32: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/runners.py:22: in _runner
    return Remote(context=_Connection("host"))
tests/runners.py:18: in _Connection
    return Connection(*args, **kwargs)
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadac953550>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: ['run']
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

runners.py::Remote_::env::augments_when_replace_env_False

runners.py::Remote_::env::augments_when_replace_env_False
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa164e50>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = , remote = None

    def augments_when_replace_env_False(self, remote):
>       env = _runner().run(CMD, env={"JUST": "ME"}, replace_env=False).env

tests/runners.py:36: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/runners.py:22: in _runner
    return Remote(context=_Connection("host"))
tests/runners.py:18: in _Connection
    return Connection(*args, **kwargs)
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa164e50>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: ['run']
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

runners.py::Remote_::run::calls_expected_paramiko_bits

runners.py::Remote_::run::calls_expected_paramiko_bits
self = , remote = None

    def calls_expected_paramiko_bits(self, remote):
        # remote mocking makes generic safety checks like "were
        # get_transport and open_session called", but we also want to make
        # sure that exec_command got run with our arg to run().
>       remote.expect(cmd=CMD)
E       AttributeError: 'NoneType' object has no attribute 'expect'

tests/runners.py:48: AttributeError

runners.py::Remote_::run::writes_remote_streams_to_local_streams

runners.py::Remote_::run::writes_remote_streams_to_local_streams
self = , remote = None

    def writes_remote_streams_to_local_streams(self, remote):
>       remote.expect(out=b"hello yes this is dog")
E       AttributeError: 'NoneType' object has no attribute 'expect'

tests/runners.py:52: AttributeError

runners.py::Remote_::run::return_value_is_Result_subclass_exposing_cxn_used

runners.py::Remote_::run::return_value_is_Result_subclass_exposing_cxn_used
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa824550>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = , remote = None

    def return_value_is_Result_subclass_exposing_cxn_used(self, remote):
>       c = _Connection("host")

tests/runners.py:58: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/runners.py:18: in _Connection
    return Connection(*args, **kwargs)
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa824550>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: ['run']
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

runners.py::Remote_::run::channel_is_closed_normally

runners.py::Remote_::run::channel_is_closed_normally
self = , remote = None

    def channel_is_closed_normally(self, remote):
>       chan = remote.expect()
E       AttributeError: 'NoneType' object has no attribute 'expect'

tests/runners.py:68: AttributeError

runners.py::Remote_::run::channel_is_closed_on_body_exceptions

runners.py::Remote_::run::channel_is_closed_on_body_exceptions
self = , remote = None

    def channel_is_closed_on_body_exceptions(self, remote):
>       chan = remote.expect()
E       AttributeError: 'NoneType' object has no attribute 'expect'

tests/runners.py:74: AttributeError

runners.py::Remote_::run::stop_calls_super_correctly

runners.py::Remote_::run::stop_calls_super_correctly
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa8db370>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = , remote = None

    def stop_calls_super_correctly(self, remote):
        # RE: #2241
        Runner.stop = Mock()
>       _runner().run(CMD)

tests/runners.py:96: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/runners.py:22: in _runner
    return Remote(context=_Connection("host"))
tests/runners.py:18: in _Connection
    return Connection(*args, **kwargs)
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa8db370>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: ['run']
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

runners.py::Remote_::run::channel_close_skipped_when_channel_not_even_made

runners.py::Remote_::run::channel_close_skipped_when_channel_not_even_made
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadab7b4820>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = 

    def channel_close_skipped_when_channel_not_even_made(self):
        # I.e. if obtaining self.channel doesn't even happen (i.e. if
        # Connection.create_session() dies), we need to account for that
        # case...
        class Oops(Exception):
            pass

        def oops():
            raise Oops

>       cxn = _Connection("host")

tests/runners.py:109: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/runners.py:18: in _Connection
    return Connection(*args, **kwargs)
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadab7b4820>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: ['run']
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

runners.py::Remote_::run::pty_True::uses_paramiko_get_pty_with_local_size

runners.py::Remote_::run::pty_True::uses_paramiko_get_pty_with_local_size
self = , remote = None

    def uses_paramiko_get_pty_with_local_size(self, remote):
>       chan = remote.expect()
E       AttributeError: 'NoneType' object has no attribute 'expect'

tests/runners.py:122: AttributeError

runners.py::Remote_::run::pty_True::no_SIGWINCH_means_no_handler

runners.py::Remote_::run::pty_True::no_SIGWINCH_means_no_handler
self = 
signal = , remote = None

    @patch("fabric.runners.signal")
    def no_SIGWINCH_means_no_handler(self, signal, remote):
        delattr(signal, "SIGWINCH")
>       remote.expect()
E       AttributeError: 'NoneType' object has no attribute 'expect'

tests/runners.py:130: AttributeError

runners.py::Remote_::run::pty_True::SIGWINCH_handled_when_present

runners.py::Remote_::run::pty_True::SIGWINCH_handled_when_present
self = 
signal = , remote = None

    @patch("fabric.runners.signal")
    def SIGWINCH_handled_when_present(self, signal, remote):
>       remote.expect()
E       AttributeError: 'NoneType' object has no attribute 'expect'

tests/runners.py:136: AttributeError

runners.py::Remote_::run::pty_True::SIGWINCH_not_handled_in_subthreads

runners.py::Remote_::run::pty_True::SIGWINCH_not_handled_in_subthreads
self = 
threading = 
signal = , remote = None

    @patch("fabric.runners.signal")
    @patch("fabric.runners.threading")
    def SIGWINCH_not_handled_in_subthreads(
        self, threading, signal, remote
    ):
>       remote.expect()
E       AttributeError: 'NoneType' object has no attribute 'expect'

tests/runners.py:153: AttributeError

runners.py::Remote_::run::pty_True::window_change_handler_uses_resize_pty

runners.py::Remote_::run::pty_True::window_change_handler_uses_resize_pty
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadab7b5420>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = 

    def window_change_handler_uses_resize_pty(self):
>       runner = _runner()

tests/runners.py:160: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/runners.py:22: in _runner
    return Remote(context=_Connection("host"))
tests/runners.py:18: in _Connection
    return Connection(*args, **kwargs)
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadab7b5420>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: ['run']
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

runners.py::Remote_::start::sends_env_to_paramiko_update_environment_by_default

runners.py::Remote_::start::sends_env_to_paramiko_update_environment_by_default
self = , remote = None

    def sends_env_to_paramiko_update_environment_by_default(self, remote):
>       chan = remote.expect()
E       AttributeError: 'NoneType' object has no attribute 'expect'

tests/runners.py:180: AttributeError

runners.py::Remote_::start::uses_export_prefixing_when_inline_env_is_True

runners.py::Remote_::start::uses_export_prefixing_when_inline_env_is_True
self = , remote = None

    def uses_export_prefixing_when_inline_env_is_True(self, remote):
>       chan = remote.expect(
            cmd="export DEBUG=1 PATH=/opt/bin && {}".format(CMD)
        )
E       AttributeError: 'NoneType' object has no attribute 'expect'

tests/runners.py:185: AttributeError

runners.py::Remote_::send_start_message_sends_exec_command

runners.py::Remote_::send_start_message_sends_exec_command
self = 

    def send_start_message_sends_exec_command(self):
        runner = Remote(context=None)
        runner.channel = Mock()
>       runner.send_start_message(command="whatever")
E       AttributeError: 'Remote' object has no attribute 'send_start_message'

tests/runners.py:195: AttributeError

runners.py::Remote_::kill_closes_the_channel

runners.py::Remote_::kill_closes_the_channel
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa1ca8c0>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = 

    def kill_closes_the_channel(self):
>       runner = _runner()

tests/runners.py:199: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/runners.py:22: in _runner
    return Remote(context=_Connection("host"))
tests/runners.py:18: in _Connection
    return Connection(*args, **kwargs)
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: ['run']\n\nValid real attributes: ['clear...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa1ca8c0>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: ['run']
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

runners.py::RemoteShell_::send_start_message_sends_invoke_shell

runners.py::RemoteShell_::send_start_message_sends_invoke_shell
self = 

    def send_start_message_sends_invoke_shell(self):
        runner = RemoteShell(context=None)
        runner.channel = Mock()
>       runner.send_start_message(command=None)
E       AttributeError: 'RemoteShell' object has no attribute 'send_start_message'

tests/runners.py:209: AttributeError

task.py::task_::accepts_Invoke_level_kwargs

task.py::task_::accepts_Invoke_level_kwargs
self = 

    def accepts_Invoke_level_kwargs(self):
        # Arbitrarily selected list of invoke-level kwargs...
        def body(c, parts):
            "I am a docstring"
            pass

        # Faux @task()
>       t = fabric.task(
            name="dadbod",
            aliases=["heavenly", "check", "shop"],
            default=True,
            help={"parts": "See: the sum of"},
            iterable=["parts"],
        )(body)
E       TypeError: 'NoneType' object is not callable

tests/task.py:46: TypeError

task.py::task_::returns_Fabric_level_Task_instance

task.py::task_::returns_Fabric_level_Task_instance
self = 

    def returns_Fabric_level_Task_instance(self):
>       assert isinstance(fabric.task(Mock()), fabric.Task)
E       AssertionError: assert False
E        +  where False = isinstance(None, )
E        +    where None = ()
E        +      where  = fabric.task
E        +      and    = Mock()
E        +    and    = fabric.Task

tests/task.py:62: AssertionError

task.py::task_::does_not_touch_klass_kwarg_if_explicitly_given

task.py::task_::does_not_touch_klass_kwarg_if_explicitly_given
self = 

    def does_not_touch_klass_kwarg_if_explicitly_given(self):
        # Otherwise sub-subclassers would be screwed, yea?
        class SubFabTask(fabric.Task):
            pass

>       assert isinstance(fabric.task(klass=SubFabTask)(Mock()), SubFabTask)
E       TypeError: 'NoneType' object is not callable

tests/task.py:69: TypeError

task.py::task_::hosts_kwarg::values_may_be_connection_first_posarg_strings

task.py::task_::hosts_kwarg::values_may_be_connection_first_posarg_strings
self = 

    def values_may_be_connection_first_posarg_strings(self):
>       self._run(["host1", "user@host2", "host3:2222"])

tests/task.py:83: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
hosts = ['host1', 'user@host2', 'host3:2222']

    def _run(self, hosts):
        @fabric.task(hosts=hosts)
>       def mytask(c):
E       TypeError: 'NoneType' object is not callable

tests/task.py:77: TypeError

task.py::task_::hosts_kwarg::values_may_be_Connection_constructor_kwarg_dicts

task.py::task_::hosts_kwarg::values_may_be_Connection_constructor_kwarg_dicts
self = 

    def values_may_be_Connection_constructor_kwarg_dicts(self):
>       self._run(
            [
                {"host": "host1"},
                {"host": "host2", "user": "user"},
                {"host": "host3", "port": 2222},
            ]
        )

tests/task.py:86: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
hosts = [{'host': 'host1'}, {'host': 'host2', 'user': 'user'}, {'host': 'host3', 'port': 2222}]

    def _run(self, hosts):
        @fabric.task(hosts=hosts)
>       def mytask(c):
E       TypeError: 'NoneType' object is not callable

tests/task.py:77: TypeError

task.py::task_::hosts_kwarg::values_may_be_mixed

task.py::task_::hosts_kwarg::values_may_be_mixed
self = 

    def values_may_be_mixed(self):
>       self._run([{"host": "host1"}, "user@host2"])

tests/task.py:95: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
hosts = [{'host': 'host1'}, 'user@host2']

    def _run(self, hosts):
        @fabric.task(hosts=hosts)
>       def mytask(c):
E       TypeError: 'NoneType' object is not callable

tests/task.py:77: TypeError

testing.py::MockRemote_::mocks_by_default::prevents_real_ssh_connectivity_via_paramiko_guts

testing.py::MockRemote_::mocks_by_default::prevents_real_ssh_connectivity_via_paramiko_guts
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa76e950>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = 
socket = 

    @patch("paramiko.transport.socket")
    def prevents_real_ssh_connectivity_via_paramiko_guts(self, socket):
        with MockRemote():
>           cxn = Connection(host="host")

tests/testing.py:32: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa76e950>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: []
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

testing.py::MockRemote_::mocks_by_default::does_not_run_safety_checks_when_nothing_really_expected

testing.py::MockRemote_::mocks_by_default::does_not_run_safety_checks_when_nothing_really_expected
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa12b850>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = 

    def does_not_run_safety_checks_when_nothing_really_expected(self):
        with MockRemote():
>           cxn = Connection(host="host")

tests/testing.py:42: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa12b850>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: []
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

testing.py::MockRemote_::enable_sftp::does_not_break_ssh_mocking

testing.py::MockRemote_::enable_sftp::does_not_break_ssh_mocking
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa76e980>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = 

    def does_not_break_ssh_mocking(self):
        with MockRemote(enable_sftp=True) as mr:
            mr.expect(cmd="whoami")
>           cxn = Connection(host="whatevs")

tests/testing.py:67: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa76e980>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: []
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

testing.py::MockRemote_::enable_sftp::enables_per_session_sftp_mocks

testing.py::MockRemote_::enable_sftp::enables_per_session_sftp_mocks
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadab5186d0>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = 

    def enables_per_session_sftp_mocks(self):
        with MockRemote(enable_sftp=True) as mr:
            mr.expect(
                cmd="rm file",
                transfers=[
                    dict(
                        method="put",
                        localpath="/local/whatevs",
                        remotepath="/remote/whatevs",
                    )
                ],
            )
>           cxn = Connection(host="host")

tests/testing.py:85: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadab5186d0>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: []
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

testing.py::MockRemote_::enable_sftp::safety_checks_work

testing.py::MockRemote_::enable_sftp::safety_checks_work
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa174550>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = 

    def safety_checks_work(self):
        with raises(AssertionError, match=r"put(.*whatevs)"):
            with MockRemote(enable_sftp=True) as mr:
                mr.expect(
                    transfers=[
                        dict(
                            method="put",
                            localpath="/local/whatevs",
                            remotepath="/remote/whatevs",
                        )
                    ],
                )
>               cxn = Connection(host="host")

tests/testing.py:101: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa174550>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: []
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

transfer.py::Transfer_::init::requires_connection

transfer.py::Transfer_::init::requires_connection
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadab77afb0>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = 

    def requires_connection(self):
        # Transfer() -> explodes
        try:
            Transfer()
        except TypeError:
            pass
        else:
            assert False, "Did not raise ArgumentError"
        # Transfer(Connection()) -> happy, exposes an attribute
>       cxn = Connection("host")

tests/transfer.py:28: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadab77afb0>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: []
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

transfer.py::Transfer_::is_remote_dir::returns_bool_of_stat_ISDIR_flag

transfer.py::Transfer_::is_remote_dir::returns_bool_of_stat_ISDIR_flag
self = 
sftp_objs = None

    def returns_bool_of_stat_ISDIR_flag(self, sftp_objs):
>       xfer, sftp = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:33: TypeError

transfer.py::Transfer_::is_remote_dir::returns_False_if_stat_raises_IOError

transfer.py::Transfer_::is_remote_dir::returns_False_if_stat_raises_IOError
self = 
sftp_objs = None

    def returns_False_if_stat_raises_IOError(self, sftp_objs):
>       xfer, sftp = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:41: TypeError

transfer.py::Transfer_::get::basics::accepts_single_remote_path_posarg

transfer.py::Transfer_::get::basics::accepts_single_remote_path_posarg
self = 
sftp_objs = None

    def accepts_single_remote_path_posarg(self, sftp_objs):
>       transfer, client = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:48: TypeError

transfer.py::Transfer_::get::basics::accepts_local_and_remote_kwargs

transfer.py::Transfer_::get::basics::accepts_local_and_remote_kwargs
self = 
sftp_objs = None

    def accepts_local_and_remote_kwargs(self, sftp_objs):
>       transfer, client = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:55: TypeError

transfer.py::Transfer_::get::basics::returns_rich_Result_object

transfer.py::Transfer_::get::basics::returns_rich_Result_object
self = 
sftp_objs = None

    def returns_rich_Result_object(self, sftp_objs):
>       transfer, client = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:62: TypeError

transfer.py::Transfer_::get::path_arg_edge_cases::local_None_uses_remote_filename

transfer.py::Transfer_::get::path_arg_edge_cases::local_None_uses_remote_filename
self = 
transfer = None

    def local_None_uses_remote_filename(self, transfer):
>       assert transfer.get("file").local == "/local/file"
E       AttributeError: 'NoneType' object has no attribute 'get'

tests/transfer.py:75: AttributeError

transfer.py::Transfer_::get::path_arg_edge_cases::local_empty_string_uses_remote_filename

transfer.py::Transfer_::get::path_arg_edge_cases::local_empty_string_uses_remote_filename
self = 
transfer = None

    def local_empty_string_uses_remote_filename(self, transfer):
>       assert transfer.get("file", local="").local == "/local/file"
E       AttributeError: 'NoneType' object has no attribute 'get'

tests/transfer.py:78: AttributeError

transfer.py::Transfer_::get::path_arg_edge_cases::remote_arg_is_required

transfer.py::Transfer_::get::path_arg_edge_cases::remote_arg_is_required
self = 
transfer = None

    @raises(TypeError)
    def remote_arg_is_required(self, transfer):
>       transfer.get()
E       AttributeError: 'NoneType' object has no attribute 'get'

tests/transfer.py:82: AttributeError

transfer.py::Transfer_::get::path_arg_edge_cases::remote_arg_cannot_be_None

transfer.py::Transfer_::get::path_arg_edge_cases::remote_arg_cannot_be_None
self = 
transfer = None

    @raises(ValueError)
    def remote_arg_cannot_be_None(self, transfer):
>       transfer.get(None)
E       AttributeError: 'NoneType' object has no attribute 'get'

tests/transfer.py:86: AttributeError

transfer.py::Transfer_::get::path_arg_edge_cases::remote_arg_cannot_be_empty_string

transfer.py::Transfer_::get::path_arg_edge_cases::remote_arg_cannot_be_empty_string
self = 
transfer = None

    @raises(ValueError)
    def remote_arg_cannot_be_empty_string(self, transfer):
>       transfer.get("")
E       AttributeError: 'NoneType' object has no attribute 'get'

tests/transfer.py:90: AttributeError

transfer.py::Transfer_::get::local_arg_interpolation::connection_params

transfer.py::Transfer_::get::local_arg_interpolation::connection_params
self = 
transfer = None

    def connection_params(self, transfer):
>       result = transfer.get("somefile", "{user}@{host}-{port}")
E       AttributeError: 'NoneType' object has no attribute 'get'

tests/transfer.py:94: AttributeError

transfer.py::Transfer_::get::local_arg_interpolation::connection_params_as_dir

transfer.py::Transfer_::get::local_arg_interpolation::connection_params_as_dir
self = 
transfer = None

    def connection_params_as_dir(self, transfer):
>       result = transfer.get("somefile", "{host}/")
E       AttributeError: 'NoneType' object has no attribute 'get'

tests/transfer.py:99: AttributeError

transfer.py::Transfer_::get::local_arg_interpolation::remote_path_posixpath_bits

transfer.py::Transfer_::get::local_arg_interpolation::remote_path_posixpath_bits
self = 
transfer = None

    def remote_path_posixpath_bits(self, transfer):
>       result = transfer.get(
            "parent/mid/leaf", "foo/{dirname}/bar/{basename}"
        )
E       AttributeError: 'NoneType' object has no attribute 'get'

tests/transfer.py:103: AttributeError

transfer.py::Transfer_::get::file_like_local_paths::remote_path_to_local_StringIO

transfer.py::Transfer_::get::file_like_local_paths::remote_path_to_local_StringIO
self = 
sftp_objs = None

    def remote_path_to_local_StringIO(self, sftp_objs):
>       self._get_to_stringio(sftp_objs)

tests/transfer.py:124: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
sftp_objs = None

    def _get_to_stringio(self, sftp_objs):
>       transfer, client = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:114: TypeError

transfer.py::Transfer_::get::file_like_local_paths::result_contains_fd_for_local_path

transfer.py::Transfer_::get::file_like_local_paths::result_contains_fd_for_local_path
self = 
sftp_objs = None

    def result_contains_fd_for_local_path(self, sftp_objs):
>       result, fd = self._get_to_stringio(sftp_objs)

tests/transfer.py:127: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
sftp_objs = None

    def _get_to_stringio(self, sftp_objs):
>       transfer, client = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:114: TypeError

transfer.py::Transfer_::get::mode_concerns::preserves_remote_mode_by_default

transfer.py::Transfer_::get::mode_concerns::preserves_remote_mode_by_default
self = 
sftp = None

    def preserves_remote_mode_by_default(self, sftp):
>       transfer, client, mock_os = sftp
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:137: TypeError

transfer.py::Transfer_::get::mode_concerns::allows_disabling_remote_mode_preservation

transfer.py::Transfer_::get::mode_concerns::allows_disabling_remote_mode_preservation
self = 
sftp = None

    def allows_disabling_remote_mode_preservation(self, sftp):
>       transfer, client, mock_os = sftp
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:146: TypeError

transfer.py::Transfer_::get::local_directory_creation::without_trailing_slash_means_leaf_file

transfer.py::Transfer_::get::local_directory_creation::without_trailing_slash_means_leaf_file
self = 
Path = , sftp_objs = None

    @patch("fabric.transfer.Path")
    def without_trailing_slash_means_leaf_file(self, Path, sftp_objs):
>       transfer, client = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:154: TypeError

transfer.py::Transfer_::get::local_directory_creation::with_trailing_slash_means_mkdir_entire_arg

transfer.py::Transfer_::get::local_directory_creation::with_trailing_slash_means_mkdir_entire_arg
self = 
Path = , sftp_objs = None

    @patch("fabric.transfer.Path")
    def with_trailing_slash_means_mkdir_entire_arg(
        self, Path, sftp_objs
    ):
>       transfer, client = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:169: TypeError

transfer.py::Transfer_::put::basics::accepts_single_local_path_posarg

transfer.py::Transfer_::put::basics::accepts_single_local_path_posarg
self = 
sftp_objs = None

    def accepts_single_local_path_posarg(self, sftp_objs):
>       transfer, client = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:183: TypeError

transfer.py::Transfer_::put::basics::accepts_local_and_remote_kwargs

transfer.py::Transfer_::put::basics::accepts_local_and_remote_kwargs
self = 
sftp_objs = None

    def accepts_local_and_remote_kwargs(self, sftp_objs):
>       transfer, sftp = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:190: TypeError

transfer.py::Transfer_::put::basics::returns_rich_Result_object

transfer.py::Transfer_::put::basics::returns_rich_Result_object
self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa125600>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
>           return self._get(key)

.venv/lib/python3.10/site-packages/invoke/config.py:117: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.10/site-packages/invoke/config.py:177: in _get
    value = self._config[key]
.venv/lib/python3.10/site-packages/invoke/config.py:168: in __getitem__
    return self._get(key)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , key = 'derive_shorthand'

    def _get(self, key: str) -> Any:
        # Short-circuit if pickling/copying mechanisms are asking if we've got
        # __setstate__ etc; they'll ask this w/o calling our __init__ first, so
        # we'd be in a RecursionError-causing catch-22 otherwise.
        if key in ("__setstate__",):
            raise AttributeError(key)
        # At this point we should be able to assume a self._config...
>       value = self._config[key]
E       KeyError: 'derive_shorthand'

.venv/lib/python3.10/site-packages/invoke/config.py:177: KeyError

During handling of the above exception, another exception occurred:

self = , transfer = None

    def returns_rich_Result_object(self, transfer):
>       cxn = Connection("host")

tests/transfer.py:198: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
fabric/connection.py:317: in __init__
    shorthand = self.derive_shorthand(host)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <[AttributeError("No attribute or config key found for 'user'\n\nValid keys: []\n\nValid real attributes: ['clear', 'c...runtime_path', 'set_runtime_ssh_path', 'setdefault', 'update']") raised in repr()] Connection object at 0x7eadaa125600>
key = 'derive_shorthand'

    def __getattr__(self, key: str) -> Any:
        # NOTE: due to default Python attribute-lookup semantics, "real"
        # attributes will always be yielded on attribute access and this method
        # is skipped. That behavior is good for us (it's more intuitive than
        # having a config key accidentally shadow a real attribute or method).
        try:
            return self._get(key)
        except KeyError:
            # Proxy most special vars to config for dict procotol.
            if key in self._proxies:
                return getattr(self._config, key)
            # Otherwise, raise useful AttributeError to follow getattr proto.
            err = "No attribute or config key found for {!r}".format(key)
            attrs = [x for x in dir(self.__class__) if not x.startswith("_")]
            err += "\n\nValid keys: {!r}".format(
                sorted(list(self._config.keys()))
            )
            err += "\n\nValid real attributes: {!r}".format(attrs)
>           raise AttributeError(err)
E           AttributeError: No attribute or config key found for 'derive_shorthand'
E           
E           Valid keys: []
E           
E           Valid real attributes: ['cd', 'clear', 'client', 'close', 'config', 'connect_kwargs', 'connect_timeout', 'cwd', 'forward_agent', 'forward_local', 'forward_remote', 'from_data', 'from_v1', 'gateway', 'get', 'host', 'is_connected', 'local', 'open', 'open_gateway', 'original_host', 'pop', 'popitem', 'port', 'prefix', 'put', 'run', 'setdefault', 'sftp', 'shell', 'ssh_config', 'sudo', 'transport', 'update', 'user']

.venv/lib/python3.10/site-packages/invoke/config.py:129: AttributeError

transfer.py::Transfer_::put::remote_end_is_directory::appends_local_file_basename

transfer.py::Transfer_::put::remote_end_is_directory::appends_local_file_basename
self = 
sftp_objs = None

    def appends_local_file_basename(self, sftp_objs):
>       xfer, sftp = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:210: TypeError

transfer.py::Transfer_::put::remote_end_is_directory::file_like_local_objects::name_attribute_present_appends_like_basename

transfer.py::Transfer_::put::remote_end_is_directory::file_like_local_objects::name_attribute_present_appends_like_basename
self = 
sftp_objs = None

    def name_attribute_present_appends_like_basename(
        self, sftp_objs
    ):
>       xfer, sftp = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:223: TypeError

transfer.py::Transfer_::put::remote_end_is_directory::file_like_local_objects::no_name_attribute_raises_ValueError

transfer.py::Transfer_::put::remote_end_is_directory::file_like_local_objects::no_name_attribute_raises_ValueError
self = 
sftp_objs = None

    @raises(ValueError)
    def no_name_attribute_raises_ValueError(self, sftp_objs):
>       xfer, sftp = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:234: TypeError

transfer.py::Transfer_::put::path_arg_edge_cases::remote_None_uses_local_filename

transfer.py::Transfer_::put::path_arg_edge_cases::remote_None_uses_local_filename
self = 
transfer = None

    def remote_None_uses_local_filename(self, transfer):
>       assert transfer.put("file").remote == "/remote/file"
E       AttributeError: 'NoneType' object has no attribute 'put'

tests/transfer.py:241: AttributeError

transfer.py::Transfer_::put::path_arg_edge_cases::remote_empty_string_uses_local_filename

transfer.py::Transfer_::put::path_arg_edge_cases::remote_empty_string_uses_local_filename
self = 
transfer = None

    def remote_empty_string_uses_local_filename(self, transfer):
>       assert transfer.put("file", remote="").remote == "/remote/file"
E       AttributeError: 'NoneType' object has no attribute 'put'

tests/transfer.py:244: AttributeError

transfer.py::Transfer_::put::path_arg_edge_cases::remote_cant_be_empty_if_local_file_like

transfer.py::Transfer_::put::path_arg_edge_cases::remote_cant_be_empty_if_local_file_like
self = 
transfer = None

    @raises(ValueError)
    def remote_cant_be_empty_if_local_file_like(self, transfer):
>       transfer.put(StringIO())
E       AttributeError: 'NoneType' object has no attribute 'put'

tests/transfer.py:248: AttributeError

transfer.py::Transfer_::put::path_arg_edge_cases::local_arg_is_required

transfer.py::Transfer_::put::path_arg_edge_cases::local_arg_is_required
self = 
transfer = None

    @raises(TypeError)
    def local_arg_is_required(self, transfer):
>       transfer.put()
E       AttributeError: 'NoneType' object has no attribute 'put'

tests/transfer.py:252: AttributeError

transfer.py::Transfer_::put::path_arg_edge_cases::local_arg_cannot_be_None

transfer.py::Transfer_::put::path_arg_edge_cases::local_arg_cannot_be_None
self = 
transfer = None

    @raises(ValueError)
    def local_arg_cannot_be_None(self, transfer):
>       transfer.put(None)
E       AttributeError: 'NoneType' object has no attribute 'put'

tests/transfer.py:256: AttributeError

transfer.py::Transfer_::put::path_arg_edge_cases::local_arg_cannot_be_empty_string

transfer.py::Transfer_::put::path_arg_edge_cases::local_arg_cannot_be_empty_string
self = 
transfer = None

    @raises(ValueError)
    def local_arg_cannot_be_empty_string(self, transfer):
>       transfer.put("")
E       AttributeError: 'NoneType' object has no attribute 'put'

tests/transfer.py:260: AttributeError

transfer.py::Transfer_::put::file_like_local_paths::remote_path_from_local_StringIO

transfer.py::Transfer_::put::file_like_local_paths::remote_path_from_local_StringIO
self = 
sftp_objs = None

    def remote_path_from_local_StringIO(self, sftp_objs):
>       self._put_from_stringio(sftp_objs)

tests/transfer.py:276: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
sftp_objs = None

    def _put_from_stringio(self, sftp_objs):
>       transfer, client = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:266: TypeError

transfer.py::Transfer_::put::file_like_local_paths::local_FLOs_are_rewound_before_putting

transfer.py::Transfer_::put::file_like_local_paths::local_FLOs_are_rewound_before_putting
self = 
transfer = None

    def local_FLOs_are_rewound_before_putting(self, transfer):
        fd = Mock()
        fd.tell.return_value = 17
>       transfer.put(fd, remote="file")
E       AttributeError: 'NoneType' object has no attribute 'put'

tests/transfer.py:281: AttributeError

transfer.py::Transfer_::put::file_like_local_paths::result_contains_fd_for_local_path

transfer.py::Transfer_::put::file_like_local_paths::result_contains_fd_for_local_path
self = 
sftp_objs = None

    def result_contains_fd_for_local_path(self, sftp_objs):
>       result, fd = self._put_from_stringio(sftp_objs)

tests/transfer.py:286: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = 
sftp_objs = None

    def _put_from_stringio(self, sftp_objs):
>       transfer, client = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:266: TypeError

transfer.py::Transfer_::put::mode_concerns::preserves_local_mode_by_default

transfer.py::Transfer_::put::mode_concerns::preserves_local_mode_by_default
self = 
sftp = None

    def preserves_local_mode_by_default(self, sftp):
>       transfer, client, mock_os = sftp
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:292: TypeError

transfer.py::Transfer_::put::mode_concerns::allows_disabling_local_mode_preservation

transfer.py::Transfer_::put::mode_concerns::allows_disabling_local_mode_preservation
self = 
sftp_objs = None

    def allows_disabling_local_mode_preservation(self, sftp_objs):
>       transfer, client = sftp_objs
E       TypeError: cannot unpack non-iterable NoneType object

tests/transfer.py:299: TypeError

util.py::get_local_user_::defaults_to_getpass_getuser

util.py::get_local_user_::defaults_to_getpass_getuser
self = 
getuser = 

    @patch("getpass.getuser")
    def defaults_to_getpass_getuser(self, getuser):
        "defaults to getpass.getuser"
        get_local_user()
>       getuser.assert_called_once_with()

tests/util.py:17: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = , args = (), kwargs = {}
msg = "Expected 'getuser' to be called once. Called 0 times."

    def assert_called_once_with(self, /, *args, **kwargs):
        """assert that the mock was called exactly once and that that call was
        with the specified arguments."""
        if not self.call_count == 1:
            msg = ("Expected '%s' to be called once. Called %s times.%s"
                   % (self._mock_name or 'mock',
                      self.call_count,
                      self._calls_repr()))
>           raise AssertionError(msg)
E           AssertionError: Expected 'getuser' to be called once. Called 0 times.

/usr/lib/python3.10/unittest/mock.py:940: AssertionError

Patch diff

diff --git a/fabric/connection.py b/fabric/connection.py
index b00bf937..96d8bc7a 100644
--- a/fabric/connection.py
+++ b/fabric/connection.py
@@ -125,7 +125,36 @@ class Connection(Context):

         .. versionadded:: 2.4
         """
-        pass
+        # Sanity check
+        if not hasattr(env, 'get'):
+            raise InvalidV1Env("'env' must be a dict-like object!")
+
+        # Map v1 settings to v2 kwargs
+        settings = {}
+        
+        # Host connection settings
+        if env.get('host_string'):
+            settings['host'] = env['host_string']
+        if env.get('user'):
+            settings['user'] = env['user']
+        if env.get('port'):
+            settings['port'] = env['port']
+        
+        # SSH settings
+        if env.get('key_filename'):
+            settings['connect_kwargs'] = {'key_filename': env['key_filename']}
+        if env.get('forward_agent'):
+            settings['forward_agent'] = env['forward_agent']
+        if env.get('gateway'):
+            settings['gateway'] = env['gateway']
+        if env.get('connect_timeout'):
+            settings['connect_timeout'] = env['connect_timeout']
+
+        # Update with any explicit kwargs
+        settings.update(kwargs)
+
+        # Create & return
+        return cls(**settings)

     def __init__(self, host, user=None, port=None, config=None, gateway=None, forward_agent=None, connect_timeout=None, connect_kwargs=None, inline_ssh_env=None):
         """
@@ -355,7 +384,7 @@ class Connection(Context):

         .. versionadded:: 2.0
         """
-        pass
+        return self.transport is not None and self.transport.active

     def open(self):
         """
@@ -380,7 +409,32 @@ class Connection(Context):
             Now returns the inner Paramiko connect call's return value instead
             of always returning the implicit ``None``.
         """
-        pass
+        if self.is_connected:
+            return None
+
+        # Set up gateway if needed
+        sock = None
+        if self.gateway:
+            sock = self.open_gateway()
+
+        # Connect!
+        connect_kwargs = dict(
+            hostname=self.host,
+            port=int(self.port),
+            username=self.user,
+            timeout=self.connect_timeout,
+            sock=sock,
+            **self.connect_kwargs
+        )
+
+        result = self.client.connect(**connect_kwargs)
+        self.transport = self.client.get_transport()
+
+        # Set up agent forwarding if requested
+        if self.forward_agent:
+            self._agent_handler = AgentRequestHandler(self.transport.get_channel("session", timeout=self.connect_timeout))
+
+        return result

     def open_gateway(self):
         """
@@ -393,7 +447,19 @@ class Connection(Context):

         .. versionadded:: 2.0
         """
-        pass
+        if isinstance(self.gateway, str):
+            # ProxyCommand
+            return ProxyCommand(self.gateway)
+        else:
+            # Jump host
+            if not self.gateway.is_connected:
+                self.gateway.open()
+            chan = self.gateway.transport.open_channel(
+                'direct-tcpip',
+                (self.host, self.port),
+                ('', 0)
+            )
+            return chan

     def close(self):
         """
@@ -407,7 +473,21 @@ class Connection(Context):
         .. versionchanged:: 3.0
             Now closes SFTP sessions too (2.x required manually doing so).
         """
-        pass
+        if self._sftp is not None:
+            self._sftp.close()
+            self._sftp = None
+
+        if self._agent_handler is not None:
+            self._agent_handler.close()
+            self._agent_handler = None
+
+        if self.transport is not None:
+            self.transport.close()
+            self.transport = None
+
+        if self.client is not None:
+            self.client.close()
+            self.client = None

     def __enter__(self):
         return self
@@ -415,8 +495,28 @@ class Connection(Context):
     def __exit__(self, *exc):
         self.close()

-    @opens
     def run(self, command, **kwargs):
+        """
+        Execute a remote command.
+
+        :param str command: The command to run.
+        :param kwargs: Additional keyword arguments to pass to `invoke.context.Context.run`.
+
+        :returns: The result of the command execution.
+        """
+        if not self.is_connected:
+            self.open()
+        channel = self.transport.open_session()
+        channel.settimeout(self.connect_timeout)
+        channel.exec_command(command)
+        stdout = channel.makefile('r')
+        stderr = channel.makefile_stderr('r')
+        status = channel.recv_exit_status()
+        return {
+            'stdout': stdout.read(),
+            'stderr': stderr.read(),
+            'status': status
+        }
         """
         Execute a shell command on the remote end of this connection.

@@ -432,8 +532,19 @@ class Connection(Context):
         """
         pass

-    @opens
     def sudo(self, command, **kwargs):
+        """
+        Execute a command with sudo.
+
+        :param str command: The command to run with sudo.
+        :param kwargs: Additional keyword arguments to pass to `run`.
+
+        :returns: The result of the command execution.
+        """
+        if not self.is_connected:
+            self.open()
+        sudo_command = f"sudo {command}"
+        return self.run(sudo_command, **kwargs)
         """
         Execute a shell command, via ``sudo``, on the remote end.

@@ -446,9 +557,36 @@ class Connection(Context):
         """
         pass

-    @opens
     def shell(self, **kwargs):
         """
+        Open an interactive shell session on the remote end.
+
+        :param kwargs: Additional keyword arguments to pass to the shell.
+
+        :returns: None
+        """
+        if not self.is_connected:
+            self.open()
+        channel = self.transport.open_session()
+        channel.get_pty(**kwargs)
+        channel.invoke_shell()
+        channel.settimeout(None)  # Disable timeout for interactive shell
+        
+        # Forward stdin/stdout
+        try:
+            while True:
+                if channel.recv_ready():
+                    data = channel.recv(1024)
+                    if not data:
+                        break
+                    print(data.decode(), end='', flush=True)
+                if channel.exit_status_ready():
+                    break
+        except KeyboardInterrupt:
+            channel.close()
+        finally:
+            channel.close()
+        """
         Run an interactive login shell on the remote end, as with ``ssh``.

         This method is intended strictly for use cases where you can't know
@@ -519,8 +657,19 @@ class Connection(Context):
         """
         pass

-    @opens
     def sftp(self):
+        """
+        Return a new SFTP session object.
+
+        The session object is a wrapper around Paramiko's SFTPClient class.
+
+        :returns: A new SFTP session object.
+        """
+        if not self.is_connected:
+            self.open()
+        if self._sftp is None:
+            self._sftp = self.transport.open_sftp_client()
+        return self._sftp
         """
         Return a `~paramiko.sftp_client.SFTPClient` object.

@@ -556,8 +705,28 @@ class Connection(Context):
         pass

     @contextmanager
-    @opens
     def forward_local(self, local_port, remote_port=None, remote_host='localhost', local_host='localhost'):
+        """
+        Create a local port forward.
+
+        :param local_port: Local port to forward.
+        :param remote_port: Remote port to forward to (defaults to local_port).
+        :param remote_host: Remote host to forward to (defaults to 'localhost').
+        :param local_host: Local interface to bind to (defaults to 'localhost').
+
+        :returns: A new `~paramiko.channel.Channel` object.
+        """
+        if not self.is_connected:
+            self.open()
+        if remote_port is None:
+            remote_port = local_port
+
+        return self.transport.request_port_forward(
+            local_host,
+            local_port,
+            remote_host,
+            remote_port
+        )
         """
         Open a tunnel connecting ``local_port`` to the server's environment.

@@ -601,8 +770,28 @@ class Connection(Context):
         pass

     @contextmanager
-    @opens
     def forward_remote(self, remote_port, local_port=None, remote_host='127.0.0.1', local_host='localhost'):
+        """
+        Create a remote port forward.
+
+        :param remote_port: Remote port to forward.
+        :param local_port: Local port to forward to (defaults to remote_port).
+        :param remote_host: Remote interface to bind to (defaults to '127.0.0.1').
+        :param local_host: Local host to forward to (defaults to 'localhost').
+
+        :returns: A new `~paramiko.channel.Channel` object.
+        """
+        if not self.is_connected:
+            self.open()
+        if local_port is None:
+            local_port = remote_port
+
+        return self.transport.request_port_forward(
+            remote_host,
+            remote_port,
+            local_host,
+            local_port
+        )
         """
         Open a tunnel connecting ``remote_port`` to the local environment.