Black run

This commit is contained in:
James Barnsley
2020-02-06 10:12:28 +13:00
parent e23a3b65ec
commit 4ae58cdf9e
7 changed files with 151 additions and 188 deletions

View File

@ -40,8 +40,8 @@ class Extension(ext.Extension):
# Add web extension
registry.add(
"http:app", {
"name": self.ext_name, "factory": iris_factory})
"http:app", {"name": self.ext_name, "factory": iris_factory}
)
# Add our frontend
registry.add("frontend", IrisFrontend)

View File

@ -119,8 +119,9 @@ class IrisCore(pykka.ThreadingActor):
# @return string
##
def generateGuid(self):
return "".join(random.choices(
string.ascii_uppercase + string.digits, k=12))
return "".join(
random.choices(string.ascii_uppercase + string.digits, k=12)
)
##
# Digest a protocol header into it's id/name parts
@ -242,8 +243,11 @@ class IrisCore(pykka.ThreadingActor):
if send_to_this_connection:
connection["connection"].write_message(json_encode(message))
response = {"message": "Broadcast to " +
str(len(self.connections)) + " connections"}
response = {
"message": "Broadcast to "
+ str(len(self.connections))
+ " connections"
}
if callback:
callback(response)
else:
@ -286,8 +290,8 @@ class IrisCore(pykka.ThreadingActor):
self.broadcast(
data={
"method": "connection_added",
"params": {
"connection": client}}
"params": {"connection": client},
}
)
def update_connection(self, *args, **kwargs):
@ -304,11 +308,11 @@ class IrisCore(pykka.ThreadingActor):
data={
"method": "connection_changed",
"params": {
"connection":
self.connections[connection_id]["client"]},
})
response = {
"connection": self.connections[connection_id]["client"]}
"connection": self.connections[connection_id]["client"]
},
}
)
response = {"connection": self.connections[connection_id]["client"]}
if callback:
callback(response)
else:
@ -350,12 +354,14 @@ class IrisCore(pykka.ThreadingActor):
data={
"method": "connection_changed",
"params": {
"connection":
self.connections[connection_id]["client"]},
})
"connection": self.connections[connection_id]["client"]
},
}
)
response = {
"connection_id": connection_id,
"username": data["username"]}
"username": data["username"],
}
if callback:
callback(response)
else:
@ -469,15 +475,11 @@ class IrisCore(pykka.ThreadingActor):
if error:
self.broadcast(data={"method": "restart_error", "params": error})
elif update:
self.broadcast(
data={
"method": "restart_updated",
"params": update})
self.broadcast(data={"method": "restart_updated", "params": update})
else:
self.broadcast(
data={
"method": "restart_finished",
"params": response})
data={"method": "restart_finished", "params": response}
)
##
# Run an upgrade of Iris
@ -502,15 +504,11 @@ class IrisCore(pykka.ThreadingActor):
if error:
self.broadcast(data={"method": "upgrade_error", "params": error})
elif update:
self.broadcast(
data={
"method": "upgrade_updated",
"params": update})
self.broadcast(data={"method": "upgrade_updated", "params": update})
else:
self.broadcast(
data={
"method": "upgrade_finished",
"params": response})
data={"method": "upgrade_finished", "params": response}
)
self.restart()
##
@ -522,10 +520,7 @@ class IrisCore(pykka.ThreadingActor):
ioloop = kwargs.get("ioloop", False)
# Trigger the action
IrisSystemThread(
"local_scan",
ioloop,
self.local_scan_callback).start()
IrisSystemThread("local_scan", ioloop, self.local_scan_callback).start()
self.broadcast(data={"method": "local_scan_started"})
@ -537,20 +532,15 @@ class IrisCore(pykka.ThreadingActor):
def local_scan_callback(self, response, error, update):
if error:
self.broadcast(
data={
"method": "local_scan_error",
"params": error})
self.broadcast(data={"method": "local_scan_error", "params": error})
elif update:
self.broadcast(
data={
"method": "local_scan_updated",
"params": update})
data={"method": "local_scan_updated", "params": update}
)
else:
self.broadcast(
data={
"method": "local_scan_finished",
"params": response})
data={"method": "local_scan_finished", "params": response}
)
##
# Spotify Radio
@ -624,15 +614,15 @@ class IrisCore(pykka.ThreadingActor):
self.broadcast(
data={
"method": "radio_started",
"params": {
"radio": self.radio}}
"params": {"radio": self.radio},
}
)
else:
self.broadcast(
data={
"method": "radio_changed",
"params": {
"radio": self.radio}}
"params": {"radio": self.radio},
}
)
self.get_radio(callback=callback)
@ -691,29 +681,30 @@ class IrisCore(pykka.ThreadingActor):
url = (
url
+ "?seed_artists="
+ (",".join(self.radio["seed_artists"])
).replace("spotify:artist:", "")
+ (",".join(self.radio["seed_artists"])).replace(
"spotify:artist:", ""
)
)
url = (
url
+ "&seed_genres="
+ (",".join(self.radio["seed_genres"])
).replace("spotify:genre:", "")
+ (",".join(self.radio["seed_genres"])).replace(
"spotify:genre:", ""
)
)
url = (
url
+ "&seed_tracks="
+ (",".join(self.radio["seed_tracks"])
).replace("spotify:track:", "")
+ (",".join(self.radio["seed_tracks"])).replace(
"spotify:track:", ""
)
)
url = url + "&limit=50"
http_client = AsyncHTTPClient()
try:
http_response = await http_client.fetch(
url, "POST", headers={
"Authorization": "Bearer " + access_token
}
url, "POST", headers={"Authorization": "Bearer " + access_token}
)
response_body = json.loads(http_response.body)
@ -730,8 +721,8 @@ class IrisCore(pykka.ThreadingActor):
+ error["error_description"]
}
logger.error(
"Could not fetch Spotify recommendations: " +
error["error_description"]
"Could not fetch Spotify recommendations: "
+ error["error_description"]
)
logger.debug(error)
return False
@ -810,17 +801,19 @@ class IrisCore(pykka.ThreadingActor):
for tlid in data["tlids"]:
item = {
"tlid": tlid,
"added_from":
data["added_from"] if "added_from" in data else None,
"added_by":
data["added_by"] if "added_by" in data else None,
"added_from": data["added_from"]
if "added_from" in data
else None,
"added_by": data["added_by"] if "added_by" in data else None,
}
self.queue_metadata["tlid_" + str(tlid)] = item
self.broadcast(
data={
"method": "queue_metadata_changed",
"params": {"queue_metadata": self.queue_metadata},
"params": {
"queue_metadata": self.queue_metadata
},
}
)
@ -873,7 +866,9 @@ class IrisCore(pykka.ThreadingActor):
data={
"method": "commands_changed",
"params": {
"commands": self.commands}}
"commands": self.commands
},
}
)
response = {"message": "Commands saved"}
@ -890,8 +885,8 @@ class IrisCore(pykka.ThreadingActor):
if str(data["id"]) not in self.commands:
error = {
"message": "Command failed",
"description": r'''Could not find command by ID
"' + str(data["id"]) + '"''',
"description": "Could not find command by ID "
+ '"' + str(data["id"]) + '"',
}
else:
command = self.commands[str(data["id"])]
@ -921,8 +916,7 @@ class IrisCore(pykka.ThreadingActor):
d = command["additional_headers"].split("\n")
lines = list(filter(lambda x: x.find(":") > 0, d))
fields = [
(x.split(":", 1)[0].strip().lower(),
x.split(":", 1)[1].strip())
(x.split(":", 1)[0].strip().lower(), x.split(":", 1)[1].strip())
for x in lines
]
headers = dict(fields)
@ -948,7 +942,8 @@ class IrisCore(pykka.ThreadingActor):
command["url"],
connect_timeout=5,
validate_cert=False,
headers=headers)
headers=headers,
)
# Make the request, and handle any request errors
try:
@ -969,14 +964,13 @@ class IrisCore(pykka.ThreadingActor):
# Perhaps it requires unicode encoding?
try:
command_response_body = tornado.escape.to_unicode(
command_response.body)
command_response.body
)
except BaseException:
command_response_body = ""
# Finally, return the result
response = {
"message": "Command run",
"response": command_response_body}
response = {"message": "Command run", "response": command_response_body}
if callback:
callback(response)
@ -998,8 +992,8 @@ class IrisCore(pykka.ThreadingActor):
# Expired, so go get a new one
if (
not self.spotify_token or
self.spotify_token["expires_at"] <= time.time()
not self.spotify_token
or self.spotify_token["expires_at"] <= time.time()
):
await self.refresh_spotify_token()
@ -1036,7 +1030,9 @@ class IrisCore(pykka.ThreadingActor):
self.broadcast(
data={
"method": "spotify_token_changed",
"params": {"spotify_token": self.spotify_token},
"params": {
"spotify_token": self.spotify_token
},
}
)
@ -1049,8 +1045,9 @@ class IrisCore(pykka.ThreadingActor):
except (urllib.error.HTTPError, urllib.error.URLError) as e:
error = json.loads(e.read())
error = {
"message": "Could not refresh token: " +
error["error_description"]}
"message": "Could not refresh token: "
+ error["error_description"]
}
if callback:
callback(False, error)
@ -1094,9 +1091,9 @@ class IrisCore(pykka.ThreadingActor):
if connection_id not in self.connections:
error = {
"message": "Unauthorized request",
"description": "Connection " +
connection_id +
" not connected",
"description": "Connection "
+ connection_id
+ " not connected",
}
except Exception as e:
@ -1113,10 +1110,8 @@ class IrisCore(pykka.ThreadingActor):
http_client = AsyncHTTPClient()
http_response = await http_client.fetch(url)
callback(
http_response.body.decode(
"utf-8",
errors="replace"),
False)
http_response.body.decode("utf-8", errors="replace"), False
)
except (urllib.error.HTTPError, urllib.error.URLError) as e:
error = json.loads(e.read())
@ -1125,8 +1120,8 @@ class IrisCore(pykka.ThreadingActor):
+ error["error_description"]
}
logger.error(
"Could not fetch Spotify recommendations: " +
error["error_description"]
"Could not fetch Spotify recommendations: "
+ error["error_description"]
)
logger.debug(error)
return error
@ -1155,7 +1150,4 @@ class IrisCore(pykka.ThreadingActor):
elif error:
self.broadcast(data={"method": "test_updated", "params": update})
else:
self.broadcast(
data={
"method": "test_finished",
"params": response})
self.broadcast(data={"method": "test_finished", "params": response})

View File

@ -23,9 +23,7 @@ class IrisFrontend(pykka.ThreadingActor, CoreListener):
iris.stop()
def track_playback_ended(self, tl_track, time_position):
iris.ioloop.add_callback(
functools.partial(
iris.check_for_radio_update))
iris.ioloop.add_callback(functools.partial(iris.check_for_radio_update))
def tracklist_changed(self):
iris.ioloop.add_callback(functools.partial(iris.clean_queue_metadata))

View File

@ -31,7 +31,8 @@ class WebsocketHandler(tornado.websocket.WebSocketHandler):
# Get the client's IP. If it's local, then use it's proxy origin
ip = self.request.remote_ip
if ip == "127.0.0.1" and hasattr(
self.request.headers, "X-Forwarded-For"):
self.request.headers, "X-Forwarded-For"
):
ip = self.request.headers["X-Forwarded-For"]
# Construct our initial client object, and add to our list of
@ -62,8 +63,8 @@ class WebsocketHandler(tornado.websocket.WebSocketHandler):
error={
"id": id,
"code": 32602,
"message": r'''Invalid JSON-RPC request (missing
property "jsonrpc")''',
"message": r"""Invalid JSON-RPC request (missing
property "jsonrpc")""",
},
)
@ -87,12 +88,12 @@ class WebsocketHandler(tornado.websocket.WebSocketHandler):
# For async methods we need to await, but it must be
# ommited for syncronous methods
if asyncio.iscoroutinefunction(
getattr(iris, message["method"])):
getattr(iris, message["method"])
):
await getattr(iris, message["method"])(
ioloop=self.ioloop,
data=params,
callback=lambda response,
error=False: self.handle_result(
callback=lambda response, error=False: self.handle_result(
id=id,
method=message["method"],
response=response,
@ -100,13 +101,10 @@ class WebsocketHandler(tornado.websocket.WebSocketHandler):
),
)
else:
getattr(
iris,
message["method"])(
getattr(iris, message["method"])(
ioloop=self.ioloop,
data=params,
callback=lambda response,
error=False: self.handle_result(
callback=lambda response, error=False: self.handle_result(
id=id,
method=message["method"],
response=response,
@ -121,9 +119,9 @@ class WebsocketHandler(tornado.websocket.WebSocketHandler):
error={
"id": id,
"code": 32601,
"message": 'Method "' +
message["method"] +
'" does not exist',
"message": 'Method "'
+ message["method"]
+ '" does not exist',
},
id=id,
)
@ -179,8 +177,8 @@ class HttpHandler(tornado.web.RequestHandler):
self.set_header("Access-Control-Allow-Origin", "*")
self.set_header(
"Access-Control-Allow-Headers",
r'''Origin, X-Requested-With, Content-Type, Accept,
Authorization, Client-Security-Token, Accept-Encoding''',
r"""Origin, X-Requested-With, Content-Type, Accept,
Authorization, Client-Security-Token, Accept-Encoding""",
)
def initialize(self, core, config):
@ -208,23 +206,16 @@ class HttpHandler(tornado.web.RequestHandler):
await getattr(iris, slug)(
ioloop=self.ioloop,
request=self,
callback=lambda response,
error=False: self.handle_result(
callback=lambda response, error=False: self.handle_result(
id=id, method=slug, response=response, error=error
),
)
else:
getattr(
iris,
slug)(
getattr(iris, slug)(
ioloop=self.ioloop,
request=self,
callback=lambda response,
error=False: self.handle_result(
id=id,
method=slug,
response=response,
error=error
callback=lambda response, error=False: self.handle_result(
id=id, method=slug, response=response, error=error
),
)
except Exception as e:
@ -235,9 +226,8 @@ class HttpHandler(tornado.web.RequestHandler):
id=id,
error={
"code": 32601,
"message": "Method " +
slug +
" does not exist"},
"message": "Method " + slug + " does not exist",
},
)
return
@ -249,8 +239,8 @@ class HttpHandler(tornado.web.RequestHandler):
params = json.loads(self.request.body.decode("utf-8"))
except BaseException:
self.handle_result(
id=id, error={
"code": 32700, "message": "Missing or invalid payload"}
id=id,
error={"code": 32700, "message": "Missing or invalid payload"},
)
return
@ -261,30 +251,23 @@ class HttpHandler(tornado.web.RequestHandler):
await getattr(iris, slug)(
data=params,
request=self.request,
callback=lambda response=False,
error=False: self.handle_result(
callback=lambda response=False, error=False: self.handle_result(
id=id, method=slug, response=response, error=error
),
)
else:
getattr(
iris,
slug)(
getattr(iris, slug)(
data=params,
request=self.request,
callback=lambda response=False,
error=False: self.handle_result(
id=id,
method=slug,
response=response,
error=error
callback=lambda response=False, error=False: self.handle_result(
id=id, method=slug, response=response, error=error
),
)
except tornado.web.HTTPError:
self.handle_result(
id=id, error={
"code": 32601, "message": "Invalid JSON payload"}
id=id,
error={"code": 32601, "message": "Invalid JSON payload"},
)
return
@ -293,9 +276,8 @@ class HttpHandler(tornado.web.RequestHandler):
id=id,
error={
"code": 32601,
"message": "Method " +
slug +
" does not exist"},
"message": "Method " + slug + " does not exist",
},
)
return
@ -322,10 +304,8 @@ class HttpHandler(tornado.web.RequestHandler):
# Digest JSON responses into JSON
content_type = response.headers.get("Content-Type")
if content_type.startswith(
"application/json"
) or content_type.startswith(
"text/json"
):
"application/json"
) or content_type.startswith("text/json"):
body = json.loads(response.body)
# Non-JSON so just copy as-is

View File

@ -17,9 +17,10 @@ class IrisSystemPermissionError(IrisSystemError):
def __init__(self, path):
message = (
r'''Password-less access to %s was refused.
Check your /etc/sudoers file.''' %
path.as_uri())
r"""Password-less access to %s was refused.
Check your /etc/sudoers file."""
% path.as_uri()
)
logger.error(message)
super().__init__(message)
@ -67,7 +68,8 @@ class IrisSystemThread(Thread):
command = self.get_command()
logger.debug("Running '%s'", os.fsdecode(b" ".join(command)))
process = subprocess.Popen(
command, stdout=subprocess.PIPE, encoding="utf8")
command, stdout=subprocess.PIPE, encoding="utf8"
)
lines = ""
while True:
@ -95,13 +97,14 @@ class IrisSystemThread(Thread):
def can_run(self, *args, **kwargs):
# Attempt an empty call to our system file
command_bytes = b" ".join(
self.get_command(
"check", non_interactive=True))
self.get_command("check", non_interactive=True)
)
process = subprocess.Popen(
command_bytes,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True)
shell=True,
)
result, error = process.communicate()
exitCode = process.wait()

View File

@ -11,7 +11,7 @@ from mopidy_iris import handlers
from mopidy_iris import core
from mopidy_iris.mem import iris
def async_return_helper(result):
f = Future()
f.set_result(result)
@ -22,22 +22,13 @@ class HttpHandlerTest(tornado.testing.AsyncHTTPTestCase):
@pytest.fixture(autouse=True)
def inject_fixtures(self, caplog):
self._caplog = caplog
def get_app(self):
http_handler = handlers.HttpHandler
# http_handler.handle_result = mock.Mock()
# self.handler_mock = http_handler.handle_result
return tornado.web.Application(
[
(
r"/(.*)",
http_handler,
{
'core': None,
'config': {},
},
)
]
[(r"/(.*)", http_handler, {"core": None, "config": {},},)]
)
def test_get_method(self):
@ -69,7 +60,7 @@ class HttpHandlerTest(tornado.testing.AsyncHTTPTestCase):
iris_mock.foo = mock.Mock()
response = self.fetch("/foo", method="GET")
iris_mock.foo.assert_called_once()
assert 200 == response.code
@ -78,14 +69,14 @@ class HttpHandlerTest(tornado.testing.AsyncHTTPTestCase):
iris_mock.foo = mock.Mock(side_effect=Exception("bar"))
response = self.fetch("/foo", method="GET")
iris_mock.foo.assert_called_once()
assert 200 == response.code
assert "bar" in self._caplog.text
@mock.patch.object(handlers.iris, "do_fetch")
def test_get_method_with_fetch(self, fetch_mock):
iris.config = {"spotify" : {"client_id": 123, "client_secret": 456}}
iris.config = {"spotify": {"client_id": 123, "client_secret": 456}}
result = mock.Mock(spec=HTTPResponse, body='{"expires_in":88}')
fetch_mock.return_value = async_return_helper(result)

View File

@ -5,54 +5,55 @@ from mopidy_iris.system import IrisSystemThread, IrisSystemPermissionError
def test_system_sh_path():
iris_system = IrisSystemThread('foo', None, None)
iris_system = IrisSystemThread("foo", None, None)
assert iris_system.script_path.is_file()
assert iris_system.script_path.name == "system.sh"
def test_can_run():
iris_system = IrisSystemThread('foo', None, None)
iris_system = IrisSystemThread("foo", None, None)
iris_system._USE_SUDO = False
assert iris_system.can_run() is True
@pytest.fixture
def popen_mock():
patcher = mock.patch("subprocess.Popen", spec=True)
yield patcher.start()
patcher.stop()
@pytest.fixture
def process_mock(popen_mock):
mock_process = popen_mock.return_value
mock_process.communicate.return_value = ('', None)
mock_process.communicate.return_value = ("", None)
mock_process.wait.return_value = 0
yield mock_process
def test_can_run_args(popen_mock, process_mock):
IrisSystemThread('foo', None, None).can_run()
IrisSystemThread("foo", None, None).can_run()
popen_mock.assert_called_once_with(
mock.ANY,
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE
mock.ANY, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE
)
def test_can_run_uses_sudo_non_interactive(popen_mock, process_mock):
IrisSystemThread('foo', None, None).can_run()
IrisSystemThread("foo", None, None).can_run()
popen_mock.assert_called_once()
assert popen_mock.call_args[0][0].startswith(b"sudo -n ")
def test_can_run_calls_script_check(popen_mock, process_mock):
IrisSystemThread('foo', None, None).can_run()
IrisSystemThread("foo", None, None).can_run()
assert popen_mock.call_args[0][0].endswith(b"system.sh check")
def test_can_run_sudo_refused_raises(popen_mock, process_mock, caplog):
process_mock.wait.return_value = 1
iris_system = IrisSystemThread('foo', None, None)
iris_system = IrisSystemThread("foo", None, None)
with pytest.raises(IrisSystemPermissionError) as excinfo:
iris_system.can_run()
@ -66,20 +67,18 @@ def test_can_run_sudo_refused_raises(popen_mock, process_mock, caplog):
def test_run_args(popen_mock, process_mock):
iris_system = IrisSystemThread('foo', mock.Mock(), None)
iris_system.can_run = mock.Mock(return_value = True)
iris_system = IrisSystemThread("foo", mock.Mock(), None)
iris_system.can_run = mock.Mock(return_value=True)
iris_system.run()
popen_mock.assert_called_once_with(
mock.ANY,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE
mock.ANY, stderr=subprocess.PIPE, stdout=subprocess.PIPE
)
def test_run_uses_sudo(popen_mock, process_mock):
iris_system = IrisSystemThread('foo', mock.Mock(), None)
iris_system.can_run = mock.Mock(return_value = True)
iris_system = IrisSystemThread("foo", mock.Mock(), None)
iris_system.can_run = mock.Mock(return_value=True)
iris_system.run()
popen_mock.assert_called_once()