Skip to content

back to Claude Sonnet 3.5 - Fill-in summary

Claude Sonnet 3.5 - Fill-in: fabric

Failed to run pytests for test tests

ImportError while loading conftest '/testbed/tests/conftest.py'.
tests/conftest.py:10: in <module>
    from fabric.testing.fixtures import client, remote, sftp, sftp_objs, transfer
fabric/__init__.py:3: in <module>
    from .connection import Config, Connection
fabric/connection.py:18: in <module>
    class Connection(Context):
fabric/connection.py:469: in Connection
    @opens
E   NameError: name 'opens' is not defined

Patch diff

diff --git a/fabric/auth.py b/fabric/auth.py
index b3460d6..70725ff 100644
--- a/fabric/auth.py
+++ b/fabric/auth.py
@@ -54,4 +54,5 @@ class OpenSSHAuthStrategy(AuthStrategy):
         """
         Shut down any resources we ourselves opened up.
         """
-        pass
+        if self.agent:
+            self.agent.close()
diff --git a/fabric/config.py b/fabric/config.py
index e501ed7..7078e85 100644
--- a/fabric/config.py
+++ b/fabric/config.py
@@ -60,7 +60,24 @@ class Config(InvokeConfig):

         .. versionadded:: 2.4
         """
-        pass
+        overrides = kwargs.pop('overrides', {})
+        v1_config = {
+            'user': env.get('user'),
+            'port': env.get('port'),
+            'hosts': env.get('hosts', []),
+            'connect_kwargs': {
+                'key_filename': env.get('key_filename'),
+                'password': env.get('password'),
+            },
+            'timeouts': {
+                'connect': env.get('timeout', 10),
+            },
+            'forward_agent': env.get('forward_agent', False),
+            'gateway': env.get('gateway'),
+            'use_ssh_config': env.get('use_ssh_config', False),
+        }
+        merged_overrides = merge_dicts(v1_config, overrides)
+        return cls(overrides=merged_overrides, **kwargs)

     def __init__(self, *args, **kwargs):
         """
@@ -117,7 +134,7 @@ class Config(InvokeConfig):

         .. versionadded:: 2.0
         """
-        pass
+        self._set(_runtime_ssh_path=path)

     def load_ssh_config(self):
         """
@@ -128,7 +145,12 @@ class Config(InvokeConfig):

         .. versionadded:: 2.0
         """
-        pass
+        # Check if an explicit SSHConfig object was given
+        if self._given_explicit_object:
+            return
+
+        # Load SSH config files
+        self._load_ssh_files()

     def _load_ssh_files(self):
         """
@@ -139,7 +161,12 @@ class Config(InvokeConfig):

         :returns: ``None``.
         """
-        pass
+        runtime_path = self._runtime_ssh_path
+        if runtime_path:
+            self._load_ssh_file(runtime_path)
+        else:
+            self._load_ssh_file(self._system_ssh_path)
+            self._load_ssh_file(os.path.expanduser(self._user_ssh_path))

     def _load_ssh_file(self, path):
         """
@@ -149,7 +176,13 @@ class Config(InvokeConfig):

         :returns: ``None``.
         """
-        pass
+        if os.path.isfile(path):
+            try:
+                with open(path) as fd:
+                    self.base_ssh_config.parse(fd)
+            except IOError as e:
+                if e.errno != errno.EACCES:
+                    raise

     @staticmethod
     def global_defaults():
@@ -169,4 +202,23 @@ class Config(InvokeConfig):
             Added the ``authentication`` settings section, plus sub-attributes
             such as ``authentication.strategy_class``.
         """
-        pass
+        defaults = InvokeConfig.global_defaults()
+        fabric_defaults = {
+            'runner': Remote,
+            'runners': {
+                'remote': Remote,
+            },
+            'timeouts': {
+                'connect': 10,
+            },
+            'ssh_config_path': None,
+            'forward_agent': False,
+            'gateway': None,
+            'use_ssh_config': True,
+            'connect_kwargs': {},
+            'authentication': {
+                'strategy_class': None,
+            },
+        }
+        merge_dicts(defaults, fabric_defaults)
+        return defaults
diff --git a/fabric/connection.py b/fabric/connection.py
index be567e3..d6c5b0b 100644
--- a/fabric/connection.py
+++ b/fabric/connection.py
@@ -362,7 +362,7 @@ class Connection(Context):

         .. versionadded:: 2.0
         """
-        pass
+        return self.client is not None and self.client.get_transport() is not None and self.client.get_transport().is_active()

     def open(self):
         """
@@ -387,7 +387,27 @@ 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
+
+        if self.gateway:
+            sock = self.open_gateway()
+        else:
+            sock = None
+
+        connect_kwargs = self.connect_kwargs.copy()
+        connect_kwargs.update({
+            'hostname': self.host,
+            'port': self.port,
+            'username': self.user,
+            'timeout': self.connect_timeout,
+            'allow_agent': self.forward_agent,
+            'sock': sock,
+        })
+
+        result = self.client.connect(**connect_kwargs)
+        self.transport = self.client.get_transport()
+        return result

     def open_gateway(self):
         """
@@ -400,7 +420,17 @@ class Connection(Context):

         .. versionadded:: 2.0
         """
-        pass
+        if isinstance(self.gateway, Connection):
+            self.gateway.open()
+            return self.gateway.transport.open_channel(
+                'direct-tcpip',
+                (self.host, self.port),
+                ('', 0)
+            )
+        elif isinstance(self.gateway, str):
+            return ProxyCommand(self.gateway)
+        else:
+            raise ValueError("Unsupported gateway type")

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

     def __enter__(self):
         return self
diff --git a/fabric/exceptions.py b/fabric/exceptions.py
index 0343cf1..eb3d284 100644
--- a/fabric/exceptions.py
+++ b/fabric/exceptions.py
@@ -1,4 +1,7 @@
 class NothingToDo(Exception):
+    """
+    Exception raised when there is nothing to do in a given operation.
+    """
     pass


@@ -7,6 +10,8 @@ class GroupException(Exception):
     Lightweight exception wrapper for `.GroupResult` when one contains errors.

     .. versionadded:: 2.0
+
+    :param result: The GroupResult object containing the errors.
     """

     def __init__(self, result):
diff --git a/fabric/executor.py b/fabric/executor.py
index 2bb1601..720e233 100644
--- a/fabric/executor.py
+++ b/fabric/executor.py
@@ -38,7 +38,15 @@ class Executor(invoke.Executor):

         :returns: Homogenous list of Connection init kwarg dicts.
         """
-        pass
+        normalized = []
+        for host in hosts:
+            if isinstance(host, str):
+                normalized.append({'host': host})
+            elif isinstance(host, dict):
+                normalized.append(host)
+            else:
+                raise ValueError(f"Invalid host type: {type(host)}")
+        return normalized

     def parameterize(self, call, connection_init_kwargs):
         """
@@ -53,4 +61,12 @@ class Executor(invoke.Executor):
         :returns:
             `.ConnectionCall`.
         """
-        pass
+        from fabric import Connection
+        connection = Connection(**connection_init_kwargs)
+        return ConnectionCall(
+            task=call.task,
+            args=call.args,
+            kwargs=call.kwargs,
+            config=call.config,
+            connection=connection
+        )
diff --git a/fabric/group.py b/fabric/group.py
index f30a24c..9a9a71d 100644
--- a/fabric/group.py
+++ b/fabric/group.py
@@ -87,7 +87,9 @@ class Group(list):

         .. versionadded:: 2.0
         """
-        pass
+        group = cls()
+        group.extend(connections)
+        return group

     def run(self, *args, **kwargs):
         """
@@ -97,7 +99,13 @@ class Group(list):

         .. versionadded:: 2.0
         """
-        pass
+        result = GroupResult()
+        for connection in self:
+            try:
+                result[connection] = connection.run(*args, **kwargs)
+            except Exception as e:
+                result[connection] = e
+        return result

     def sudo(self, *args, **kwargs):
         """
@@ -107,7 +115,13 @@ class Group(list):

         .. versionadded:: 2.6
         """
-        pass
+        result = GroupResult()
+        for connection in self:
+            try:
+                result[connection] = connection.sudo(*args, **kwargs)
+            except Exception as e:
+                result[connection] = e
+        return result

     def put(self, *args, **kwargs):
         """
@@ -123,7 +137,13 @@ class Group(list):

         .. versionadded:: 2.6
         """
-        pass
+        result = GroupResult()
+        for connection in self:
+            try:
+                result[connection] = connection.put(*args, **kwargs)
+            except Exception as e:
+                result[connection] = e
+        return result

     def get(self, *args, **kwargs):
         """
@@ -150,7 +170,15 @@ class Group(list):

         .. versionadded:: 2.6
         """
-        pass
+        result = GroupResult()
+        for connection in self:
+            try:
+                if 'local' not in kwargs:
+                    kwargs['local'] = f"{connection.host}/"
+                result[connection] = connection.get(*args, **kwargs)
+            except Exception as e:
+                result[connection] = e
+        return result

     def close(self):
         """
@@ -158,7 +186,8 @@ class Group(list):

         .. versionadded:: 2.4
         """
-        pass
+        for connection in self:
+            connection.close()

     def __enter__(self):
         return self
@@ -216,7 +245,7 @@ class GroupResult(dict):

         .. versionadded:: 2.0
         """
-        pass
+        return {k: v for k, v in self.items() if not isinstance(v, Exception)}

     @property
     def failed(self):
@@ -225,4 +254,4 @@ class GroupResult(dict):

         .. versionadded:: 2.0
         """
-        pass
+        return {k: v for k, v in self.items() if isinstance(v, Exception)}
diff --git a/fabric/main.py b/fabric/main.py
index 18c45ce..be1c07b 100644
--- a/fabric/main.py
+++ b/fabric/main.py
@@ -13,7 +13,17 @@ from . import Config, Executor


 class Fab(Program):
-    pass
+    def __init__(self, version=None, namespace=None, name=None):
+        super().__init__(version=version, namespace=namespace, name=name)
+        self.config_class = Config
+        self.executor_class = Executor


+def make_program():
+    return Fab(
+        version=f"Fabric {fabric} (Invoke {invoke}) (Paramiko {paramiko})",
+        namespace=Collection.from_module(Path(__file__).parent / "tasks"),
+        name="fab",
+    )
+
 program = make_program()
diff --git a/fabric/runners.py b/fabric/runners.py
index 53763d2..1e15080 100644
--- a/fabric/runners.py
+++ b/fabric/runners.py
@@ -43,11 +43,62 @@ class Remote(Runner):

         Sends a window resize command via Paramiko channel method.
         """
-        pass
+        if self.channel and self.channel.get_transport().is_active():
+            rows, cols = pty_size()
+            self.channel.resize_pty(width=cols, height=rows)


 class RemoteShell(Remote):
-    pass
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.channel = None
+
+    def start(self):
+        """
+        Start an interactive shell session.
+        """
+        self.channel = self.context.client.invoke_shell()
+        self.channel.settimeout(self.context.config.timeouts.command)
+
+        # Set up window size
+        rows, cols = pty_size()
+        self.channel.resize_pty(width=cols, height=rows)
+
+        # Set up SIGWINCH handler
+        signal.signal(signal.SIGWINCH, self.handle_window_change)
+
+    def stop(self):
+        """
+        Stop the interactive shell session.
+        """
+        if self.channel:
+            self.channel.close()
+            self.channel = None
+
+    def run(self, command):
+        """
+        Run a command in the interactive shell.
+        """
+        if not self.channel:
+            self.start()
+
+        self.channel.send(command + "\n")
+        return self._receive_output()
+
+    def _receive_output(self):
+        """
+        Receive and return output from the channel.
+        """
+        output = ""
+        while True:
+            if self.channel.recv_ready():
+                chunk = self.channel.recv(4096).decode("utf-8")
+                output += chunk
+                if self.channel.exit_status_ready():
+                    break
+            if not self.channel.recv_ready() and self.channel.exit_status_ready():
+                break
+        return output


 class Result(InvokeResult):
diff --git a/fabric/tasks.py b/fabric/tasks.py
index 093c8e7..055fcab 100644
--- a/fabric/tasks.py
+++ b/fabric/tasks.py
@@ -62,7 +62,13 @@ def task(*args, **kwargs):

     .. versionadded:: 2.1
     """
-    pass
+    def wrapper(func):
+        hosts = kwargs.pop('hosts', None)
+        task_obj = invoke.task(*args, **kwargs)(func)
+        if hosts:
+            task_obj = Task(task_obj, hosts=hosts)
+        return task_obj
+    return wrapper if callable(args[0]) else wrapper


 class ConnectionCall(invoke.Call):
@@ -81,9 +87,9 @@ class ConnectionCall(invoke.Call):
             Keyword arguments used to create a new `.Connection` when the
             wrapped task is executed. Default: ``None``.
         """
-        init_kwargs = kwargs.pop('init_kwargs')
+        init_kwargs = kwargs.pop('init_kwargs', None)
         super().__init__(*args, **kwargs)
-        self.init_kwargs = init_kwargs
+        self.init_kwargs = init_kwargs or {}

     def __repr__(self):
         ret = super().__repr__()
diff --git a/fabric/testing/base.py b/fabric/testing/base.py
index c232e49..0903fd5 100644
--- a/fabric/testing/base.py
+++ b/fabric/testing/base.py
@@ -61,7 +61,12 @@ class Command:

         .. versionadded:: 2.7
         """
-        pass
+        if self.cmd is not None:
+            channel.exec_command.assert_called_with(self.cmd)
+        channel.recv.side_effect = [self.out]
+        channel.recv_stderr.side_effect = [self.err]
+        channel.recv_exit_status.return_value = self.exit
+        channel.exit_status_ready.side_effect = chain(repeat(False, self.waits), repeat(True))


 class ShellCommand(Command):
@@ -184,7 +189,23 @@ class Session:

         .. versionadded:: 2.1
         """
-        pass
+        self.client = Mock()
+        self.channels = []
+
+        for command in self.commands:
+            channel = MockChannel(stdout=command.out, stderr=command.err)
+            channel.recv_exit_status.return_value = command.exit
+            channel.exit_status_ready.side_effect = chain(repeat(False, command.waits), repeat(True))
+            self.channels.append(channel)
+
+        self.client.get_transport.return_value.open_session.side_effect = self.channels
+        
+        if self.host:
+            self.client.connect.assert_called_with(self.host, username=self.user, port=self.port)
+
+        if self._enable_sftp:
+            self.sftp = Mock()
+            self.client.open_sftp.return_value = self.sftp

     def stop(self):
         """
@@ -192,7 +213,10 @@ class Session:

         .. versionadded:: 3.2
         """
-        pass
+        if hasattr(self, 'client'):
+            self.client.close()
+        if hasattr(self, 'sftp'):
+            self.sftp.close()


 class MockRemote:
@@ -234,7 +258,9 @@ class MockRemote:

         .. versionadded:: 2.1
         """
-        pass
+        session = Session(*args, **kwargs, enable_sftp=self._enable_sftp)
+        channels = self.expect_sessions(session)
+        return channels[0] if channels else None

     def expect_sessions(self, *sessions):
         """
@@ -244,7 +270,13 @@ class MockRemote:

         .. versionadded:: 2.1
         """
-        pass
+        self.sessions = sessions
+        channels = []
+        for session in self.sessions:
+            session.generate_mocks()
+            channels.extend(session.channels)
+        self.start()
+        return channels

     def start(self):
         """
@@ -252,7 +284,14 @@ class MockRemote:

         .. versionadded:: 2.1
         """
-        pass
+        self.patcher = patch('paramiko.SSHClient', autospec=True)
+        mock_client = self.patcher.start()
+        mock_client.return_value = self.sessions[0].client if self.sessions else Mock()
+        
+        if self._enable_sftp:
+            self.sftp_patcher = patch('paramiko.SFTPClient', autospec=True)
+            mock_sftp = self.sftp_patcher.start()
+            mock_sftp.from_transport.return_value = self.sessions[0].sftp if self.sessions else Mock()

     def stop(self):
         """
@@ -260,7 +299,12 @@ class MockRemote:

         .. versionadded:: 2.1
         """
-        pass
+        if hasattr(self, 'patcher'):
+            self.patcher.stop()
+        if hasattr(self, 'sftp_patcher'):
+            self.sftp_patcher.stop()
+        for session in self.sessions:
+            session.stop()

     @deprecated(version='3.2', reason=
         'This method has been renamed to `safety` & will be removed in 4.0')
@@ -278,7 +322,15 @@ class MockRemote:

         .. versionadded:: 3.2
         """
-        pass
+        for session in self.sessions:
+            if not session.guard_only:
+                session.client.connect.assert_called()
+                for channel, command in zip(session.channels, session.commands):
+                    command.expect_execution(channel)
+            if session._enable_sftp and session.transfers:
+                for transfer in session.transfers:
+                    method = getattr(session.sftp, transfer['method'])
+                    method.assert_called_with(**{k: v for k, v in transfer.items() if k != 'method'})

     def __enter__(self):
         return self
diff --git a/fabric/testing/fixtures.py b/fabric/testing/fixtures.py
index 1571718..b062bd0 100644
--- a/fabric/testing/fixtures.py
+++ b/fabric/testing/fixtures.py
@@ -45,7 +45,11 @@ def connection():

     .. versionadded:: 2.1
     """
-    pass
+    with patch.object(Connection, 'run', new_callable=Mock), \
+         patch.object(Connection, 'local', new_callable=Mock):
+        conn = Connection('host', user='user')
+        conn.config.run.in_stream = False
+        yield conn


 cxn = connection
@@ -60,7 +64,8 @@ def remote_with_sftp():
     functionality was called), note that the returned `MockRemote` object has a
     ``.sftp`` attribute when created in this mode.
     """
-    pass
+    with MockRemote(enable_sftp=True) as mocked:
+        yield mocked


 @fixture
@@ -75,7 +80,8 @@ def remote():

     .. versionadded:: 2.1
     """
-    pass
+    with MockRemote() as mocked:
+        yield mocked


 @fixture
@@ -91,7 +97,11 @@ def sftp():

     .. versionadded:: 2.1
     """
-    pass
+    with patch('fabric.transfer.os') as mock_os:
+        mock_sftp = Mock(spec=MockSFTP)
+        transfer = Transfer(Connection('host'))
+        transfer.sftp = mock_sftp
+        yield transfer, mock_sftp, mock_os


 @fixture
@@ -101,7 +111,8 @@ def sftp_objs(sftp):

     .. versionadded:: 2.1
     """
-    pass
+    transfer, sftp_client, _ = sftp
+    yield transfer, sftp_client


 @fixture
@@ -111,7 +122,8 @@ def transfer(sftp):

     .. versionadded:: 2.1
     """
-    pass
+    transfer, _, _ = sftp
+    yield transfer


 @fixture
@@ -154,4 +166,8 @@ def client():

     .. versionadded:: 2.1
     """
-    pass
+    with patch('paramiko.SSHClient') as mock_client:
+        mock_transport = Mock()
+        mock_transport.active = True
+        mock_client.return_value.get_transport.return_value = mock_transport
+        yield mock_client.return_value
diff --git a/fabric/transfer.py b/fabric/transfer.py
index a241aa0..3182a7c 100644
--- a/fabric/transfer.py
+++ b/fabric/transfer.py
@@ -87,7 +87,37 @@ class Transfer:
         .. versionchanged:: 2.6
             Create missing ``local`` directories automatically.
         """
-        pass
+        sftp = self.connection.sftp()
+        remote_path = posixpath.join(sftp.getcwd() or '', remote)
+        remote_basename = posixpath.basename(remote_path)
+        remote_dirname = posixpath.dirname(remote_path)
+
+        if not local:
+            local = os.getcwd()
+
+        if isinstance(local, str):
+            local = local.format(
+                host=self.connection.host,
+                user=self.connection.user,
+                port=self.connection.port,
+                basename=remote_basename,
+                dirname=remote_dirname
+            )
+            if os.path.isdir(local):
+                local = os.path.join(local, remote_basename)
+            local_dir = os.path.dirname(local)
+            if local_dir:
+                os.makedirs(local_dir, exist_ok=True)
+            with open(local, 'wb') as local_file:
+                sftp.getfo(remote_path, local_file)
+        else:
+            sftp.getfo(remote_path, local)
+
+        if preserve_mode:
+            remote_mode = sftp.stat(remote_path).st_mode
+            os.chmod(local, remote_mode)
+
+        return Result(local, local, remote_path, remote, self.connection)

     def put(self, local, remote=None, preserve_mode=True):
         """
@@ -135,7 +165,33 @@ class Transfer:

         .. versionadded:: 2.0
         """
-        pass
+        sftp = self.connection.sftp()
+
+        if isinstance(local, str):
+            local_path = os.path.expanduser(local)
+            local_basename = os.path.basename(local_path)
+            if not os.path.isfile(local_path):
+                raise OSError(f"Local file '{local_path}' does not exist")
+            local_mode = os.stat(local_path).st_mode
+        else:
+            local_path = None
+            local_basename = 'file'
+            local_mode = None
+
+        if not remote:
+            remote = local_basename
+        remote_path = posixpath.join(sftp.getcwd() or '', remote)
+
+        if isinstance(local, str):
+            sftp.put(local_path, remote_path)
+        else:
+            with sftp.file(remote_path, 'wb') as remote_file:
+                remote_file.write(local.read())
+
+        if preserve_mode and local_mode is not None:
+            sftp.chmod(remote_path, local_mode)
+
+        return Result(local_path or local, local, remote_path, remote, self.connection)


 class Result:
diff --git a/fabric/tunnels.py b/fabric/tunnels.py
index c0a117b..0a7c270 100644
--- a/fabric/tunnels.py
+++ b/fabric/tunnels.py
@@ -34,6 +34,30 @@ class TunnelManager(ExceptionHandlingThread):
         self.remote_address = remote_host, remote_port
         self.transport = transport
         self.finished = finished
+        self.local_socket = None
+
+    def run(self):
+        try:
+            self.local_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            self.local_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+            self.local_socket.bind(self.local_address)
+            self.local_socket.listen(5)
+
+            while not self.finished.is_set():
+                readable, _, _ = select.select([self.local_socket], [], [], 0.1)
+                if self.local_socket in readable:
+                    client_socket, _ = self.local_socket.accept()
+                    channel = self.transport.open_channel('direct-tcpip', self.remote_address, client_socket.getpeername())
+                    if channel is None:
+                        client_socket.close()
+                    else:
+                        tunnel = Tunnel(channel, client_socket, self.finished)
+                        tunnel.start()
+        except Exception as e:
+            self.exception = e
+        finally:
+            if self.local_socket:
+                self.local_socket.close()


 class Tunnel(ExceptionHandlingThread):
@@ -51,6 +75,22 @@ class Tunnel(ExceptionHandlingThread):
         self.channel_chunk_size = 1024
         super().__init__()

+    def run(self):
+        try:
+            while not self.finished.is_set():
+                r, w, x = select.select([self.channel, self.sock], [], [], 0.1)
+                if self.channel in r:
+                    if self.read_and_write(self.channel, self.sock, self.channel_chunk_size):
+                        break
+                if self.sock in r:
+                    if self.read_and_write(self.sock, self.channel, self.socket_chunk_size):
+                        break
+        except Exception as e:
+            self.exception = e
+        finally:
+            self.channel.close()
+            self.sock.close()
+
     def read_and_write(self, reader, writer, chunk_size):
         """
         Read ``chunk_size`` from ``reader``, writing result to ``writer``.
@@ -59,4 +99,8 @@ class Tunnel(ExceptionHandlingThread):

         .. versionadded:: 2.0
         """
-        pass
+        data = reader.recv(chunk_size)
+        if len(data) == 0:
+            return True
+        writer.sendall(data)
+        return None
diff --git a/fabric/util.py b/fabric/util.py
index f39ab25..bfc3daf 100644
--- a/fabric/util.py
+++ b/fabric/util.py
@@ -1,5 +1,7 @@
 import logging
 import sys
+import getpass
+
 log = logging.getLogger('fabric')
 for x in ('debug',):
     globals()[x] = getattr(log, x)
@@ -12,4 +14,7 @@ def get_local_user():

     .. versionadded:: 2.0
     """
-    pass
+    try:
+        return getpass.getuser()
+    except Exception:
+        return None