Skip to content

Commit dc70fcb

Browse files
authored
style: Fix and enforce formatting (#33)
This commit introduces both pycodestyle and isort tooling to the CI check pipeline. These tools ensure a consistent code style and import order across the codebase. The commit also includes a number of fixes to the codebase to ensure it passes the new checks. Most of these were automated through the use of the CLI tool `black`. The rest were fixed manually.
1 parent 0263f36 commit dc70fcb

23 files changed

+442
-318
lines changed

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ test: install
3131
.PHONY: lint
3232
lint: #! Run type analysis and linting checks
3333
lint: install
34-
poetry run mypy ld_eventsource
34+
@poetry run mypy ld_eventsource
35+
@poetry run isort --check --atomic ld_eventsource contract-tests
36+
@poetry run pycodestyle ld_eventsource contract-tests
3537

3638
#
3739
# Documentation generation

contract-tests/service.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
from stream_entity import StreamEntity
2-
31
import json
42
import logging
53
import os
64
import sys
7-
import urllib3
5+
from logging.config import dictConfig
6+
87
from flask import Flask, request
98
from flask.logging import default_handler
10-
from logging.config import dictConfig
9+
from stream_entity import StreamEntity
1110

1211
default_port = 8000
1312

@@ -30,7 +29,7 @@
3029
'handlers': ['console']
3130
},
3231
'loggers': {
33-
'werkzeug': { 'level': 'ERROR' } # disable irrelevant Flask app logging
32+
'werkzeug': {'level': 'ERROR'} # disable irrelevant Flask app logging
3433
}
3534
})
3635

@@ -54,11 +53,13 @@ def status():
5453
}
5554
return (json.dumps(body), 200, {'Content-type': 'application/json'})
5655

56+
5757
@app.route('/', methods=['DELETE'])
5858
def delete_stop_service():
5959
global_log.info("Test service has told us to exit")
6060
os._exit(0)
6161

62+
6263
@app.route('/', methods=['POST'])
6364
def post_create_stream():
6465
global stream_counter, streams
@@ -74,6 +75,7 @@ def post_create_stream():
7475

7576
return ('', 201, {'Location': resource_url})
7677

78+
7779
@app.route('/streams/<id>', methods=['POST'])
7880
def post_stream_command(id):
7981
global streams
@@ -87,6 +89,7 @@ def post_stream_command(id):
8789
return ('', 400)
8890
return ('', 204)
8991

92+
9093
@app.route('/streams/<id>', methods=['DELETE'])
9194
def delete_stream(id):
9295
global streams
@@ -97,6 +100,7 @@ def delete_stream(id):
97100
stream.close()
98101
return ('', 204)
99102

103+
100104
if __name__ == "__main__":
101105
port = default_port
102106
if sys.argv[len(sys.argv) - 1] != 'service.py':

contract-tests/stream_entity.py

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@
44
import sys
55
import threading
66
import traceback
7+
78
import urllib3
89

910
# Import ld_eventsource from parent directory
1011
sys.path.insert(1, os.path.join(sys.path[0], '..'))
11-
from ld_eventsource import *
12-
from ld_eventsource.actions import *
13-
from ld_eventsource.config import *
14-
12+
from ld_eventsource import * # noqa: E402
13+
from ld_eventsource.actions import * # noqa: E402
14+
from ld_eventsource.config import * # noqa: E402
1515

1616
http_client = urllib3.PoolManager()
1717

18+
1819
def millis_to_seconds(t):
1920
return None if t is None else t / 1000
2021

@@ -27,7 +28,7 @@ def __init__(self, options):
2728
self.closed = False
2829
self.callback_counter = 0
2930
self.sse = None
30-
31+
3132
thread = threading.Thread(target=self.run)
3233
thread.start()
3334

@@ -38,60 +39,65 @@ def run(self):
3839
connect = ConnectStrategy.http(
3940
url=stream_url,
4041
headers=self.options.get("headers"),
41-
urllib3_request_options=None if self.options.get("readTimeoutMs") is None else {
42-
"timeout": urllib3.Timeout(read=millis_to_seconds(self.options.get("readTimeoutMs")))
43-
}
44-
)
42+
urllib3_request_options=(
43+
None
44+
if self.options.get("readTimeoutMs") is None
45+
else {
46+
"timeout": urllib3.Timeout(
47+
read=millis_to_seconds(self.options.get("readTimeoutMs"))
48+
)
49+
}
50+
),
51+
)
4552
sse = SSEClient(
4653
connect,
47-
initial_retry_delay=millis_to_seconds(self.options.get("initialDelayMs")),
54+
initial_retry_delay=millis_to_seconds(
55+
self.options.get("initialDelayMs")
56+
),
4857
last_event_id=self.options.get("lastEventId"),
49-
error_strategy=ErrorStrategy.from_lambda(lambda _:
50-
(ErrorStrategy.FAIL if self.closed else ErrorStrategy.CONTINUE, None)),
51-
logger=self.log
58+
error_strategy=ErrorStrategy.from_lambda(
59+
lambda _: (
60+
ErrorStrategy.FAIL if self.closed else ErrorStrategy.CONTINUE,
61+
None,
62+
)
63+
),
64+
logger=self.log,
5265
)
5366
self.sse = sse
5467
for item in sse.all:
5568
if isinstance(item, Event):
5669
self.log.info('Received event from stream (%s)', item.event)
57-
self.send_message({
58-
'kind': 'event',
59-
'event': {
60-
'type': item.event,
61-
'data': item.data,
62-
'id': item.last_event_id
70+
self.send_message(
71+
{
72+
'kind': 'event',
73+
'event': {
74+
'type': item.event,
75+
'data': item.data,
76+
'id': item.last_event_id,
77+
},
6378
}
64-
})
79+
)
6580
elif isinstance(item, Comment):
6681
self.log.info('Received comment from stream: %s', item.comment)
67-
self.send_message({
68-
'kind': 'comment',
69-
'comment': item.comment
70-
})
82+
self.send_message({'kind': 'comment', 'comment': item.comment})
7183
elif isinstance(item, Fault):
7284
if self.closed:
7385
break
7486
# item.error will be None if this is just an EOF rather than an I/O error or HTTP error.
7587
# Currently the test harness does not expect us to send an error message in that case.
7688
if item.error:
7789
self.log.info('Received error from stream: %s' % item.error)
78-
self.send_message({
79-
'kind': 'error',
80-
'error': str(item.error)
81-
})
90+
self.send_message({'kind': 'error', 'error': str(item.error)})
8291
except Exception as e:
8392
self.log.info('Received error from stream: %s', e)
8493
self.log.info(traceback.format_exc())
85-
self.send_message({
86-
'kind': 'error',
87-
'error': str(e)
88-
})
94+
self.send_message({'kind': 'error', 'error': str(e)})
8995

9096
def do_command(self, command: str) -> bool:
9197
self.log.info('Test service sent command: %s' % command)
9298
# currently we support no special commands
9399
return False
94-
100+
95101
def send_message(self, message):
96102
global http_client
97103

@@ -104,9 +110,9 @@ def send_message(self, message):
104110
resp = http_client.request(
105111
'POST',
106112
callback_url,
107-
headers = {'Content-Type': 'application/json'},
108-
body = json.dumps(message)
109-
)
113+
headers={'Content-Type': 'application/json'},
114+
body=json.dumps(message),
115+
)
110116
if resp.status >= 300 and not self.closed:
111117
self.log.error('Callback request returned HTTP error %d', resp.status)
112118
except Exception as e:

ld_eventsource/actions.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ class Action:
66
"""
77
Base class for objects that can be returned by :attr:`.SSEClient.all`.
88
"""
9+
910
pass
1011

1112

@@ -16,11 +17,13 @@ class Event(Action):
1617
Instances of this class are returned by both :attr:`.SSEClient.events` and
1718
:attr:`.SSEClient.all`.
1819
"""
19-
def __init__(self,
20-
event: str='message',
21-
data: str='',
22-
id: Optional[str]=None,
23-
last_event_id: Optional[str]=None
20+
21+
def __init__(
22+
self,
23+
event: str = 'message',
24+
data: str = '',
25+
id: Optional[str] = None,
26+
last_event_id: Optional[str] = None,
2427
):
2528
self._event = event
2629
self._data = data
@@ -58,27 +61,31 @@ def last_event_id(self) -> Optional[str]:
5861
def __eq__(self, other):
5962
if not isinstance(other, Event):
6063
return False
61-
return self._event == other._event and self._data == other._data \
62-
and self._id == other._id and self.last_event_id == other.last_event_id
64+
return (
65+
self._event == other._event
66+
and self._data == other._data
67+
and self._id == other._id
68+
and self.last_event_id == other.last_event_id
69+
)
6370

6471
def __repr__(self):
6572
return "Event(event=\"%s\", data=%s, id=%s, last_event_id=%s)" % (
6673
self._event,
6774
json.dumps(self._data),
6875
"None" if self._id is None else json.dumps(self._id),
69-
"None" if self._last_event_id is None else json.dumps(self._last_event_id)
76+
"None" if self._last_event_id is None else json.dumps(self._last_event_id),
7077
)
7178

7279

7380
class Comment(Action):
7481
"""
7582
A comment received by :class:`.SSEClient`.
76-
83+
7784
Comment lines (any line beginning with a colon) have no significance in the SSE specification
7885
and can be ignored, but if you want to see them, use :attr:`.SSEClient.all`. They will never
7986
be returned by :attr:`.SSEClient.events`.
8087
"""
81-
88+
8289
def __init__(self, comment: str):
8390
self._comment = comment
8491

@@ -104,6 +111,7 @@ class Start(Action):
104111
A ``Start`` is returned for the first successful connection. If the client reconnects
105112
after a failure, there will be a :class:`.Fault` followed by a ``Start``.
106113
"""
114+
107115
pass
108116

109117

@@ -121,7 +129,7 @@ class Fault(Action):
121129

122130
def __init__(self, error: Optional[Exception]):
123131
self.__error = error
124-
132+
125133
@property
126134
def error(self) -> Optional[Exception]:
127135
"""

ld_eventsource/config/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
from .connect_strategy import ConnectStrategy, ConnectionClient, ConnectionResult
1+
from .connect_strategy import (ConnectionClient, ConnectionResult,
2+
ConnectStrategy)
23
from .error_strategy import ErrorStrategy
34
from .retry_delay_strategy import RetryDelayStrategy

ld_eventsource/config/connect_strategy.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from __future__ import annotations
2+
23
from logging import Logger
34
from typing import Callable, Iterator, Optional, Union
5+
46
from urllib3 import PoolManager
57

68
from ld_eventsource.http import _HttpClientImpl, _HttpConnectParams
@@ -33,9 +35,9 @@ def create_client(self, logger: Logger) -> ConnectionClient:
3335
@staticmethod
3436
def http(
3537
url: str,
36-
headers: Optional[dict]=None,
37-
pool: Optional[PoolManager]=None,
38-
urllib3_request_options: Optional[dict]=None
38+
headers: Optional[dict] = None,
39+
pool: Optional[PoolManager] = None,
40+
urllib3_request_options: Optional[dict] = None,
3941
) -> ConnectStrategy:
4042
"""
4143
Creates the default HTTP implementation, specifying request parameters.
@@ -46,7 +48,9 @@ def http(
4648
:param urllib3_request_options: optional ``kwargs`` to add to the ``request`` call; these
4749
can include any parameters supported by ``urllib3``, such as ``timeout``
4850
"""
49-
return _HttpConnectStrategy(_HttpConnectParams(url, headers, pool, urllib3_request_options))
51+
return _HttpConnectStrategy(
52+
_HttpConnectParams(url, headers, pool, urllib3_request_options)
53+
)
5054

5155

5256
class ConnectionClient:
@@ -65,7 +69,9 @@ def connect(self, last_event_id: Optional[str]) -> ConnectionResult:
6569
(should be sent to the server to support resuming an interrupted stream)
6670
:return: a :class:`ConnectionResult` representing the stream
6771
"""
68-
raise NotImplementedError("ConnectionClient base class cannot be used by itself")
72+
raise NotImplementedError(
73+
"ConnectionClient base class cannot be used by itself"
74+
)
6975

7076
def close(self):
7177
"""
@@ -80,16 +86,12 @@ def __exit__(self, type, value, traceback):
8086
self.close()
8187

8288

83-
8489
class ConnectionResult:
8590
"""
8691
The return type of :meth:`ConnectionClient.connect()`.
8792
"""
88-
def __init__(
89-
self,
90-
stream: Iterator[bytes],
91-
closer: Optional[Callable]
92-
):
93+
94+
def __init__(self, stream: Iterator[bytes], closer: Optional[Callable]):
9395
self.__stream = stream
9496
self.__closer = closer
9597

@@ -118,6 +120,7 @@ def __exit__(self, type, value, traceback):
118120
# _HttpConnectStrategy and _HttpConnectionClient are defined here rather than in http.py to avoid
119121
# a circular module reference.
120122

123+
121124
class _HttpConnectStrategy(ConnectStrategy):
122125
def __init__(self, params: _HttpConnectParams):
123126
self.__params = params

0 commit comments

Comments
 (0)