Skip to content

Commit c183f37

Browse files
committed
feat(codec): add codec interface and refactor some components
1 parent a1821b9 commit c183f37

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+4501
-319
lines changed

.github/workflows/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
enable-cache: true
5454

5555
- name: Sync environment with all extras
56-
run: " uv sync --all-extras"
56+
run: "uv sync --all-extras"
5757

5858
- name: Run Tests with Coverage
5959
run: "uv run pytest --cov=src/dubbo --cov-report=term-missing"

pyproject.toml

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@ classifiers = [
5656
]
5757

5858
dependencies = [
59-
"anyio>=4.9.0",
60-
"h2>=4.1.0",
61-
"psutil>=6.0.0",
62-
"sniffio>=1.3.1",
63-
"typing-extensions>=4.13.2; python_version < '3.10'",
59+
"anyio~=4.9.0",
60+
"h2~=4.2.0",
61+
"psutil~=7.0.0",
62+
"sniffio~=1.3.1",
63+
"typing-extensions~=4.14.0; python_version < '3.10'",
6464
]
6565

6666

@@ -71,27 +71,26 @@ Repository = "https://github.com/apache/dubbo-python"
7171
Issues = "https://github.com/apache/dubbo/issues"
7272

7373
[project.optional-dependencies]
74-
zookeeper = [
75-
"kazoo>=2.10.0",
76-
]
77-
74+
zookeeper = ["kazoo~=2.10.0"]
75+
uvloop = ["uvloop~=0.21.0; sys_platform != 'win32'"]
76+
pydantic = ["pydantic~=2.11.7"]
77+
protobuf = ["protobuf~=6.31.1"]
7878

7979
[dependency-groups]
8080
build = [
81-
"hatch>=1.14.1",
82-
"hatch-fancy-pypi-readme>=25.1.0",
81+
"hatch~=1.14.1",
82+
"hatch-fancy-pypi-readme~=25.1.0",
8383
]
8484
dev = [
85-
"pre-commit>=4.2.0",
86-
"ruff>=0.11.10",
87-
"mypy>=1.15.0",
88-
"pytest>=8.3.5",
89-
"pytest-cov>=6.1.1",
90-
"pytest-mock>=3.14.1",
85+
"pre-commit~=4.2.0",
86+
"ruff~=0.12.0",
87+
"mypy~=1.16.0",
88+
"pytest~=8.4.0",
89+
"pytest-cov~=6.2.1",
90+
"pytest-mock~=3.14.1",
9191
]
9292

9393

94-
9594
# ------------- Hatch settings -------------
9695
[tool.hatch.version]
9796
path = "src/dubbo/__about__.py"
@@ -121,7 +120,7 @@ replacement = 'src="https://raw.githubusercontent.com/apache/dubbo-python/main/\
121120

122121
[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
123122
# Replace relative links with absolute ones
124-
pattern = "\\[([^\\]]+)\\]\\((docs/[^)]+)\\)"
123+
pattern = "\\[([^\\]]+)\\]\\((docs/[^)]+)\\)"
125124
replacement = "[$1](https://github.com/apache/dubbo-python/blob/main/$2)"
126125

127126
# ------------- Ruff settings -------------

src/dubbo/cluster/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,16 @@
1313
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
16+
17+
from .base import AsyncCluster, AsyncDirectory, AsyncLoadBalance, AsyncRouter, Cluster, Directory, LoadBalance, Router
18+
19+
__all__ = [
20+
"Cluster",
21+
"AsyncCluster",
22+
"Directory",
23+
"AsyncDirectory",
24+
"LoadBalance",
25+
"AsyncLoadBalance",
26+
"Router",
27+
"AsyncRouter",
28+
]

src/dubbo/cluster/base/__init__.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one or more
3+
# contributor license agreements. See the NOTICE file distributed with
4+
# this work for additional information regarding copyright ownership.
5+
# The ASF licenses this file to You under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with
7+
# the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
from .cluster import AsyncCluster, Cluster, ClusterInvoker
18+
from .directory import AsyncDirectory, Directory
19+
from .loadbalance import AsyncLoadBalance, LoadBalance
20+
from .router import AsyncRouter, AsyncRouterFactory, Router, RouterFactory
21+
22+
__all__ = [
23+
"Cluster",
24+
"AsyncCluster",
25+
"ClusterInvoker",
26+
"Directory",
27+
"AsyncDirectory",
28+
"LoadBalance",
29+
"AsyncLoadBalance",
30+
"Router",
31+
"AsyncRouter",
32+
"RouterFactory",
33+
"AsyncRouterFactory",
34+
]

src/dubbo/cluster/base/cluster.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one or more
3+
# contributor license agreements. See the NOTICE file distributed with
4+
# this work for additional information regarding copyright ownership.
5+
# The ASF licenses this file to You under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with
7+
# the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
import abc
17+
import typing
18+
19+
from dubbo.protocol import AsyncInvoker, Invoker
20+
21+
if typing.TYPE_CHECKING:
22+
from .directory import AsyncDirectory, Directory
23+
24+
__all__ = ["Cluster", "AsyncCluster", "ClusterInvoker"]
25+
26+
27+
class ClusterInvoker(Invoker, abc.ABC):
28+
"""ClusterInvoker"""
29+
30+
@property
31+
@abc.abstractmethod
32+
def directory(self) -> "Directory":
33+
"""Get the directory"""
34+
raise NotImplementedError()
35+
36+
@property
37+
@abc.abstractmethod
38+
def destroyed(self) -> bool:
39+
"""Whether the cluster has been destroyed"""
40+
raise NotImplementedError()
41+
42+
43+
class Cluster(abc.ABC):
44+
"""Cluster interface for service invocation"""
45+
46+
@abc.abstractmethod
47+
def join(self, directory: "Directory") -> Invoker:
48+
"""Join a directory to create an invoker for service invocation.
49+
50+
Args:
51+
directory: The directory containing available invokers.
52+
53+
Returns:
54+
Invoker: An invoker that can be used to perform service calls.
55+
"""
56+
raise NotImplementedError()
57+
58+
59+
class AsyncCluster(abc.ABC):
60+
"""Asynchronous Cluster interface for service invocation"""
61+
62+
@abc.abstractmethod
63+
async def join(self, directory: "AsyncDirectory") -> AsyncInvoker:
64+
"""Asynchronously join a directory to create an invoker for service invocation.
65+
66+
Args:
67+
directory: The asynchronous directory containing available invokers.
68+
69+
Returns:
70+
AsyncInvoker: An asynchronous invoker that can be used to perform service calls.
71+
"""
72+
raise NotImplementedError()
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one or more
3+
# contributor license agreements. See the NOTICE file distributed with
4+
# this work for additional information regarding copyright ownership.
5+
# The ASF licenses this file to You under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with
7+
# the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
import abc
17+
18+
from dubbo.common import AsyncNode, Node
19+
from dubbo.protocol import AsyncInvoker, Invocation, Invoker
20+
21+
22+
class Directory(Node, abc.ABC):
23+
"""Directory"""
24+
25+
@abc.abstractmethod
26+
def list(self, invocation: Invocation) -> list[Invoker]:
27+
"""List relevant invokers based on the invocation."""
28+
raise NotImplementedError()
29+
30+
31+
class AsyncDirectory(AsyncNode, abc.ABC):
32+
"""Asynchronous Directory"""
33+
34+
@abc.abstractmethod
35+
async def list(self, invocation: Invocation) -> list[AsyncInvoker]:
36+
"""Asynchronously list relevant invokers based on the invocation."""
37+
raise NotImplementedError()
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one or more
3+
# contributor license agreements. See the NOTICE file distributed with
4+
# this work for additional information regarding copyright ownership.
5+
# The ASF licenses this file to You under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with
7+
# the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
import abc
17+
from typing import Optional
18+
19+
from dubbo.common import URL
20+
from dubbo.protocol import AsyncInvoker, Invocation, Invoker
21+
22+
23+
class LoadBalance(abc.ABC):
24+
"""Base class for load balancing strategies.
25+
26+
Defines the interface for selecting an invoker from a list of available
27+
invokers based on the load balancing algorithm implementation.
28+
"""
29+
30+
@abc.abstractmethod
31+
def select(self, invokers: list[Invoker], url: URL, invocation: Invocation) -> Optional[Invoker]:
32+
"""Select an invoker from the available invokers list.
33+
34+
Args:
35+
invokers: List of available service invokers.
36+
url: The request URL with configuration parameters.
37+
invocation: The service invocation context.
38+
39+
Returns:
40+
The selected invoker, or None if no suitable invoker is found.
41+
"""
42+
raise NotImplementedError()
43+
44+
45+
class AsyncLoadBalance(abc.ABC):
46+
"""Base class for asynchronous load balancing strategies.
47+
48+
Defines the interface for selecting an invoker from a list of available
49+
invokers based on the load balancing algorithm implementation.
50+
"""
51+
52+
@abc.abstractmethod
53+
async def select(self, invokers: list[AsyncInvoker], url: URL, invocation: Invocation) -> Optional[AsyncInvoker]:
54+
"""Asynchronously select an invoker from the available invokers list.
55+
56+
Args:
57+
invokers: List of available service invokers.
58+
url: The request URL with configuration parameters.
59+
invocation: The service invocation context.
60+
61+
Returns:
62+
The selected asynchronous invoker, or None if no suitable invoker is found.
63+
"""
64+
raise NotImplementedError()

src/dubbo/cluster/base/router.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#
2+
# Licensed to the Apache Software Foundation (ASF) under one or more
3+
# contributor license agreements. See the NOTICE file distributed with
4+
# this work for additional information regarding copyright ownership.
5+
# The ASF licenses this file to You under the Apache License, Version 2.0
6+
# (the "License"); you may not use this file except in compliance with
7+
# the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
import abc
17+
18+
from dubbo.common import URL
19+
from dubbo.protocol import AsyncInvoker, Invocation, Invoker
20+
21+
22+
class Router(abc.ABC):
23+
"""Router interface for service invocation"""
24+
25+
@property
26+
@abc.abstractmethod
27+
def priority(self) -> int:
28+
"""Get the priority of the router."""
29+
raise NotImplementedError()
30+
31+
@abc.abstractmethod
32+
def route(self, invokers: list[Invoker], url: URL, invocation: Invocation) -> list[Invoker]:
33+
"""Route the invokers based on the URL and invocation."""
34+
raise NotImplementedError()
35+
36+
@abc.abstractmethod
37+
def get_url(self) -> URL:
38+
"""Get the URL associated with this router."""
39+
raise NotImplementedError()
40+
41+
@abc.abstractmethod
42+
def notify(self, invokers: list[Invoker]) -> None:
43+
"""Notify the router of changes in the invoker list."""
44+
raise NotImplementedError()
45+
46+
47+
class RouterFactory(abc.ABC):
48+
"""RouterFactory"""
49+
50+
@abc.abstractmethod
51+
def get_router(self, url: URL) -> Router:
52+
"""Get the router."""
53+
raise NotImplementedError()
54+
55+
56+
class AsyncRouter(abc.ABC):
57+
"""Asynchronous Router interface for service invocation"""
58+
59+
@property
60+
@abc.abstractmethod
61+
def priority(self) -> int:
62+
"""Get the priority of the router."""
63+
raise NotImplementedError()
64+
65+
@abc.abstractmethod
66+
async def route(self, invokers: list[AsyncInvoker], url: URL, invocation: Invocation) -> list[AsyncInvoker]:
67+
"""Asynchronously route the invokers based on the URL and invocation."""
68+
raise NotImplementedError()
69+
70+
@abc.abstractmethod
71+
def get_url(self) -> URL:
72+
"""Get the URL associated with this router."""
73+
raise NotImplementedError()
74+
75+
@abc.abstractmethod
76+
async def notify(self, invokers: list[AsyncInvoker]) -> None:
77+
"""Notify the router of changes in the invoker list."""
78+
raise NotImplementedError()
79+
80+
81+
class AsyncRouterFactory(abc.ABC):
82+
"""AsyncRouterFactory"""
83+
84+
@abc.abstractmethod
85+
def get_router(self, url: URL) -> AsyncRouter:
86+
"""Get the router."""
87+
raise NotImplementedError()

0 commit comments

Comments
 (0)