Skip to content

Commit 700da8b

Browse files
committed
update
1 parent 763c50b commit 700da8b

File tree

7 files changed

+154
-7
lines changed

7 files changed

+154
-7
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3+
4+
name: Python package
5+
6+
on:
7+
push:
8+
branches: [master,dev]
9+
pull_request:
10+
branches: [master]
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-latest
15+
strategy:
16+
matrix:
17+
python-version: [3.6, 3.7, 3.8, 3.9]
18+
19+
steps:
20+
- uses: actions/checkout@v2
21+
- name: Set up Python ${{ matrix.python-version }}
22+
uses: actions/setup-python@v2
23+
with:
24+
python-version: ${{ matrix.python-version }}
25+
- name: Install devDependence
26+
run: |
27+
python -m pip install --upgrade pip
28+
pip install mypy pycodestyle coverage lxml
29+
- name: Install dependencies
30+
run: |
31+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
32+
- name: Lint with pep8
33+
run: |
34+
pycodestyle --max-line-length=140 --ignore=E501 --first --statistics sserender
35+
- name: Type Hint Check
36+
run: |
37+
mypy --ignore-missing-imports --show-column-numbers --follow-imports=silent --check-untyped-defs --disallow-untyped-defs --no-implicit-optional --warn-unused-ignores sserender
38+
- name: Unit Test
39+
run: |
40+
python -m coverage run --source=sserender -m unittest discover -v -s . -p *test*.py
41+
python -m coverage report

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,4 @@ dmypy.json
127127

128128
# Pyre type checker
129129
.pyre/
130+
doc_unittest

pmfprc.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
{
2-
"env": "venv"
2+
"env": "venv",
3+
"added_components": {
4+
"cp_python_test//simplemodule_tests": "tests"
5+
}
36
}

sserender/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from .sse import SSE
2+
__all__ = ["SSE"]

sserender/sse.py

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,41 @@ def __init__(self, ID: Optional[str] = None,
2929
data: Optional[str] = None,
3030
comment: Optional[str] = None,
3131
retry: Optional[int] = None) -> None:
32-
if any([ID, event, data, comment, retry]):
32+
"""构造sse消息.
33+
34+
除了retry外至少要有1个参数.
35+
36+
Args:
37+
ID (Optional[str], optional): 消息id. Defaults to None.
38+
event (Optional[str], optional): 消息事件. Defaults to None.
39+
data (Optional[str], optional): 消息数据. Defaults to None.
40+
comment (Optional[str], optional): 消息注释. Defaults to None.
41+
retry (Optional[int], optional): 重连间隔时间,单位ms. Defaults to None.
42+
43+
Raises:
44+
TypeError: [description]
45+
ValueError: [description]
46+
"""
47+
if any([ID, event, data, comment]):
3348
self.ID = ID
3449
self.event = event
3550
self.data = data
3651
self.retry = retry
3752
self.comment = comment
53+
if self.retry and not isinstance(self.retry, int):
54+
raise TypeError("retry argument must be int")
3855
else:
39-
raise ValueError("at least one args")
56+
raise ValueError("at least one argument")
57+
58+
def render(self, *, with_encode: bool = False) -> Union[str, bytes]:
59+
"""渲染sse消息对象
4060
41-
def render(self, with_encode: bool = False) -> Union[str, bytes]:
61+
Args:
62+
with_encode (bool, optional): 是否编码. Defaults to False.
63+
64+
Returns:
65+
Union[str, bytes]: with_encode为True则返回为bytes否则为str
66+
"""
4267
buffer = io.StringIO()
4368
if self.comment is not None:
4469
for chunk in self._LINE_SEP_EXPR.split(self.comment):
@@ -57,8 +82,6 @@ def render(self, with_encode: bool = False) -> Union[str, bytes]:
5782
buffer.write(self._DEFAULT_SEPARATOR)
5883

5984
if self.retry is not None:
60-
if not isinstance(self.retry, int):
61-
raise TypeError("retry argument must be int")
6285
buffer.write("retry: {}".format(self.retry))
6386
buffer.write(self._DEFAULT_SEPARATOR)
6487

@@ -69,7 +92,19 @@ def render(self, with_encode: bool = False) -> Union[str, bytes]:
6992
return result
7093

7194
@classmethod
72-
def from_content(clz, content: Union[str, bytes], strict: bool = False) -> "SSE":
95+
def from_content(clz, content: Union[str, bytes], *, strict: bool = False) -> "SSE":
96+
"""从字符串或者字节流中解析出sse消息.
97+
98+
Args:
99+
content (Union[str, bytes]): 待解析的内容
100+
strict (bool, optional): 严格模式,校验结尾是否为`\r\n\r\n`. Defaults to False.
101+
102+
Raises:
103+
ValueError: not end with `\r\n\r\n`
104+
105+
Returns:
106+
[SSE]: sse消息对象
107+
"""
73108
if isinstance(content, bytes):
74109
content = content.decode("utf-8")
75110
if strict:
@@ -82,6 +117,7 @@ def from_content(clz, content: Union[str, bytes], strict: bool = False) -> "SSE"
82117
retry = 0
83118
comment = ""
84119
lines = content.strip().split(clz._DEFAULT_SEPARATOR)
120+
print(lines)
85121
for line in lines:
86122
if line.startswith(":"):
87123
c = line[1:].strip() + "\n"

tests/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def setUpModule() -> None:
2+
print("[Module sserender Test Start]")
3+
4+
5+
def tearDownModule() -> None:
6+
print("[Module sserender Test End]")

tests/test_module.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import unittest
2+
from pathlib import Path
3+
4+
from sserender import SSE
5+
6+
7+
class ExampleTest(unittest.TestCase):
8+
9+
def setUp(self) -> None:
10+
print("section start")
11+
12+
def tearDown(self) -> None:
13+
print("section end")
14+
15+
def test_sse_render(self) -> None:
16+
instance = SSE(ID="1", event="test", data="test_data", comment="test_comment", retry=12345)
17+
res = instance.render()
18+
assert ": test_comment" in res
19+
assert "id: 1" in res
20+
assert "event: test" in res
21+
assert "data: test_data" in res
22+
assert "retry: 12345" in res
23+
24+
def test_sse_render_to_bytes(self) -> None:
25+
instance = SSE(ID="1", event="test", data="test_data", comment="test_comment", retry=12345)
26+
res = instance.render(with_encode=True).decode("utf-8")
27+
assert ": test_comment" in res
28+
assert "id: 1" in res
29+
assert "event: test" in res
30+
assert "data: test_data" in res
31+
assert "retry: 12345" in res
32+
33+
def test_sse_render_empty(self) -> None:
34+
with self.assertRaisesRegex(ValueError, r"at least one argument") as a:
35+
SSE()
36+
37+
def test_sse_render_retry_not_int(self) -> None:
38+
with self.assertRaisesRegex(TypeError, r"retry argument must be int") as a:
39+
SSE(ID="12", retry="12345")
40+
41+
def test_sse_from_content(self) -> None:
42+
instance = SSE(ID="1", event="test", data="test_data", comment="test_comment", retry=12345)
43+
content = instance.render()
44+
sse = SSE.from_content(content)
45+
print(sse.ID)
46+
assert sse.ID == "1"
47+
48+
def test_sse_from_content_bytes(self) -> None:
49+
instance = SSE(ID="1", event="test", data="test_data", comment="test_comment", retry=12345)
50+
content = instance.render(with_encode=True)
51+
sse = SSE.from_content(content)
52+
print(sse.ID)
53+
assert sse.ID == "1"
54+
55+
def test_sse_from_content_strict(self) -> None:
56+
instance = SSE(ID="1", event="test", data="test_data", comment="test_comment", retry=12345)
57+
content = instance.render().strip()
58+
with self.assertRaisesRegex(ValueError, r"not end with") as a:
59+
SSE.from_content(content, strict=True)

0 commit comments

Comments
 (0)