[tor-commits] [stem/master] Fix interpreter integ test

atagar at torproject.org atagar at torproject.org
Fri Jul 24 23:32:52 UTC 2020


commit 561b9e6eb9ddc5f966e7971fd4cb2c7bbc1695df
Author: Damian Johnson <atagar at torproject.org>
Date:   Fri Jul 24 16:01:29 2020 -0700

    Fix interpreter integ test
    
    Our integration tests are failing with...
    
      ======================================================================
      FAIL: test_running_command
      ----------------------------------------------------------------------
      Traceback (most recent call last):
        File "/home/atagar/Desktop/stem/test/require.py", line 75, in wrapped
          return func(self, *args, **kwargs)
        File "/home/atagar/Desktop/stem/test/integ/interpreter.py", line 35, in test_running_command
          self.assertEqual(expected, _run_prompt('--run', 'GETINFO config-file'))
      AssertionError: Lists differ: ['250-config-file=/home/atagar/Desktop/stem/test/data/torrc', '250 OK'] != []
    
      First list contains 2 additional elements.
      First extra element 0:
      '250-config-file=/home/atagar/Desktop/stem/test/data/torrc'
    
      - ['250-config-file=/home/atagar/Desktop/stem/test/data/torrc', '250 OK']
      + []
    
    Our actual reason surfaces in tor-prompt's stderr...
    
      % python3.7 tor-prompt --run 'GETINFO config-file' --interface 9051
      /home/atagar/Desktop/stem/stem/interpreter/__init__.py:115: RuntimeWarning: coroutine 'BaseController.__aenter__' was never awaited
        with controller:
      /home/atagar/Desktop/stem/stem/interpreter/commands.py:366: RuntimeWarning: coroutine 'BaseController.msg' was never awaited
        output = format(str(self._controller.msg(command).raw_content()).strip(), *STANDARD_OUTPUT)
      /home/atagar/Desktop/stem/stem/interpreter/__init__.py:182: RuntimeWarning: coroutine 'BaseController.__aexit__' was never awaited
        break
    
    The problem is that stem.connection.connect() returns asynchronous controllers,
    whereas callers such as the interpreter require the class to be synchronous.
    This workaround is pretty gross hackery but in the long run I expect to
    completely replace the module prior to Stem 2.x.
---
 stem/connection.py           | 32 +++++++++++++++++++++++++++++---
 stem/interpreter/__init__.py |  5 +----
 stem/interpreter/commands.py |  4 ++--
 3 files changed, 32 insertions(+), 9 deletions(-)

diff --git a/stem/connection.py b/stem/connection.py
index 1d386594..5c8f8dbe 100644
--- a/stem/connection.py
+++ b/stem/connection.py
@@ -143,11 +143,14 @@ fine-grained control over the authentication process. For instance...
 
 import asyncio
 import binascii
+import functools
 import getpass
 import hashlib
 import hmac
+import inspect
 import os
 import threading
+import unittest
 
 import stem.control
 import stem.response
@@ -266,6 +269,9 @@ def connect(control_port: Tuple[str, Union[str, int]] = ('127.0.0.1', 'default')
     **control_port** and **control_socket** are **None**
   """
 
+  if controller is None:
+    raise ValueError('Sockets can only be created within an asyncio context. Please use connect_async() instead.')
+
   # TODO: change this function's API so we can provide a concrete type
   # TODO: 'loop_thread' gets orphaned, causing the thread to linger until we
   #   change this function's API
@@ -278,9 +284,29 @@ def connect(control_port: Tuple[str, Union[str, int]] = ('127.0.0.1', 'default')
   try:
     connection = asyncio.run_coroutine_threadsafe(connect_async(control_port, control_socket, password, password_prompt, chroot_path, controller), loop).result()
 
-    if connection is None and loop_thread.is_alive():
-      loop.call_soon_threadsafe(loop.stop)
-      loop_thread.join()
+    # if connecting failed then clean up and return
+
+    if connection is None:
+      if loop_thread.is_alive():
+        loop.call_soon_threadsafe(loop.stop)
+        loop_thread.join()
+
+      return None
+
+    # Convert our controller into a synchronous instance. This is hoirribly
+    # hacky, and prior to our next release I plan to redesign much of this
+    # module...
+
+    connection._no_op = False
+    connection._loop_thread = loop_thread
+
+    for name, func in inspect.getmembers(connection):
+      if name in ('_socket_connect', '_socket_close', '__aiter__', '__aenter__', '__aexit__'):
+        pass
+      elif isinstance(func, unittest.mock.Mock) and inspect.iscoroutinefunction(func.side_effect):
+        setattr(connection, name, functools.partial(connection._run_async_method, name))
+      elif inspect.ismethod(func) and inspect.iscoroutinefunction(func):
+        setattr(connection, name, functools.partial(connection._run_async_method, name))
 
     return connection
   except:
diff --git a/stem/interpreter/__init__.py b/stem/interpreter/__init__.py
index 6666dc89..a9301329 100644
--- a/stem/interpreter/__init__.py
+++ b/stem/interpreter/__init__.py
@@ -118,10 +118,7 @@ def main() -> None:
 
     if args.run_cmd:
       if args.run_cmd.upper().startswith('SETEVENTS '):
-        async def handle_event(event_message: stem.response.ControlMessage) -> None:
-          print(format(str(event_message), *STANDARD_OUTPUT))
-
-        controller._handle_event = handle_event  # type: ignore
+        controller._handle_event = lambda event_message: print(format(str(event_message), *STANDARD_OUTPUT))  # type: ignore
 
         if sys.stdout.isatty():
           events = args.run_cmd.upper().split(' ', 1)[1]
diff --git a/stem/interpreter/commands.py b/stem/interpreter/commands.py
index b04fcc85..83fd18ef 100644
--- a/stem/interpreter/commands.py
+++ b/stem/interpreter/commands.py
@@ -130,8 +130,8 @@ class ControlInterpreter(code.InteractiveConsole):
 
     handle_event_real = self._controller._handle_event
 
-    async def handle_event_wrapper(event_message: stem.response.ControlMessage) -> None:
-      await handle_event_real(event_message)
+    def handle_event_wrapper(event_message: stem.response.ControlMessage) -> None:
+      handle_event_real(event_message)
       self._received_events.insert(0, event_message)  # type: ignore
 
       if len(self._received_events) > MAX_EVENTS:





More information about the tor-commits mailing list