Skip to content

Commit f3d19ff

Browse files
Support plugins defined as inner classes (#1318)
* Support plugins defined as inner classes * Prefer __qualname__ over __name__ for classes --------- Co-authored-by: Abhinav Singh <126065+abhinavsingh@users.noreply.github.com>
1 parent 93f6fd6 commit f3d19ff

File tree

16 files changed

+122
-35
lines changed

16 files changed

+122
-35
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support plugins defined as inner classes

proxy/common/plugins.py

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import logging
1414
import importlib
1515
import itertools
16+
from types import ModuleType
1617
from typing import Any, Dict, List, Tuple, Union, Optional
1718

1819
from .utils import text_, bytes_
@@ -75,31 +76,54 @@ def load(
7576
# this plugin_ is implementing
7677
base_klass = None
7778
for k in mro:
78-
if bytes_(k.__name__) in p:
79+
if bytes_(k.__qualname__) in p:
7980
base_klass = k
8081
break
8182
if base_klass is None:
8283
raise ValueError('%s is NOT a valid plugin' % text_(plugin_))
83-
if klass not in p[bytes_(base_klass.__name__)]:
84-
p[bytes_(base_klass.__name__)].append(klass)
85-
logger.info('Loaded plugin %s.%s', module_name, klass.__name__)
84+
if klass not in p[bytes_(base_klass.__qualname__)]:
85+
p[bytes_(base_klass.__qualname__)].append(klass)
86+
logger.info('Loaded plugin %s.%s', module_name, klass.__qualname__)
8687
# print(p)
8788
return p
8889

8990
@staticmethod
9091
def importer(plugin: Union[bytes, type]) -> Tuple[type, str]:
9192
"""Import and returns the plugin."""
9293
if isinstance(plugin, type):
93-
return (plugin, '__main__')
94+
if inspect.isclass(plugin):
95+
return (plugin, plugin.__module__ or '__main__')
96+
raise ValueError('%s is not a valid reference to a plugin class' % text_(plugin))
9497
plugin_ = text_(plugin.strip())
9598
assert plugin_ != ''
96-
module_name, klass_name = plugin_.rsplit(text_(DOT), 1)
97-
klass = getattr(
98-
importlib.import_module(
99-
module_name.replace(
100-
os.path.sep, text_(DOT),
101-
),
102-
),
103-
klass_name,
104-
)
99+
path = plugin_.split(text_(DOT))
100+
klass = None
101+
102+
def locate_klass(klass_module_name: str, klass_path: List[str]) -> Union[type, None]:
103+
klass_module_name = klass_module_name.replace(os.path.sep, text_(DOT))
104+
try:
105+
klass_module = importlib.import_module(klass_module_name)
106+
except ModuleNotFoundError:
107+
return None
108+
klass_container: Union[ModuleType, type] = klass_module
109+
for klass_path_part in klass_path:
110+
try:
111+
klass_container = getattr(klass_container, klass_path_part)
112+
except AttributeError:
113+
return None
114+
if not isinstance(klass_container, type) or not inspect.isclass(klass_container):
115+
return None
116+
return klass_container
117+
118+
module_name = None
119+
for module_name_parts in range(len(path) - 1, 0, -1):
120+
module_name = '.'.join(path[0:module_name_parts])
121+
klass = locate_klass(module_name, path[module_name_parts:])
122+
if klass:
123+
break
124+
if klass is None:
125+
module_name = '__main__'
126+
klass = locate_klass(module_name, path)
127+
if klass is None or module_name is None:
128+
raise ValueError('%s is not resolvable as a plugin class' % text_(plugin))
105129
return (klass, module_name)

proxy/core/acceptor/acceptor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ def _work(self, conn: socket.socket, addr: Optional[HostPort]) -> None:
246246
conn,
247247
addr,
248248
event_queue=self.event_queue,
249-
publisher_id=self.__class__.__name__,
249+
publisher_id=self.__class__.__qualname__,
250250
)
251251
# TODO: Move me into target method
252252
logger.debug( # pragma: no cover

proxy/core/work/fd/fd.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def work(self, *args: Any) -> None:
3939
self.works[fileno].publish_event(
4040
event_name=eventNames.WORK_STARTED,
4141
event_payload={'fileno': fileno, 'addr': addr},
42-
publisher_id=self.__class__.__name__,
42+
publisher_id=self.__class__.__qualname__,
4343
)
4444
try:
4545
self.works[fileno].initialize()

proxy/core/work/work.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def shutdown(self) -> None:
8383
self.publish_event(
8484
event_name=eventNames.WORK_FINISHED,
8585
event_payload={},
86-
publisher_id=self.__class__.__name__,
86+
publisher_id=self.__class__.__qualname__,
8787
)
8888

8989
def run(self) -> None:

proxy/http/exception/http_request_rejected.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def __init__(
3636
self.reason: Optional[bytes] = reason
3737
self.headers: Optional[Dict[bytes, bytes]] = headers
3838
self.body: Optional[bytes] = body
39-
klass_name = self.__class__.__name__
39+
klass_name = self.__class__.__qualname__
4040
super().__init__(
4141
message='%s %r' % (klass_name, reason)
4242
if reason

proxy/http/exception/proxy_auth_failed.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class ProxyAuthenticationFailed(HttpProtocolException):
2828
incoming request doesn't present necessary credentials."""
2929

3030
def __init__(self, **kwargs: Any) -> None:
31-
super().__init__(self.__class__.__name__, **kwargs)
31+
super().__init__(self.__class__.__qualname__, **kwargs)
3232

3333
def response(self, _request: 'HttpParser') -> memoryview:
3434
return PROXY_AUTH_FAILED_RESPONSE_PKT

proxy/http/exception/proxy_conn_failed.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def __init__(self, host: str, port: int, reason: str, **kwargs: Any):
2929
self.host: str = host
3030
self.port: int = port
3131
self.reason: str = reason
32-
super().__init__('%s %s' % (self.__class__.__name__, reason), **kwargs)
32+
super().__init__('%s %s' % (self.__class__.__qualname__, reason), **kwargs)
3333

3434
def response(self, _request: 'HttpParser') -> memoryview:
3535
return BAD_GATEWAY_RESPONSE_PKT

proxy/http/proxy/plugin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def name(self) -> str:
5151
5252
Defaults to name of the class. This helps plugin developers to directly
5353
access a specific plugin by its name."""
54-
return self.__class__.__name__ # pragma: no cover
54+
return self.__class__.__qualname__ # pragma: no cover
5555

5656
def resolve_dns(self, host: str, port: int) -> Tuple[Optional[str], Optional['HostPort']]:
5757
"""Resolve upstream server host to an IP address.

proxy/http/proxy/server.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,7 @@ def emit_request_complete(self) -> None:
883883
if self.request.method == httpMethods.POST
884884
else None,
885885
},
886-
publisher_id=self.__class__.__name__,
886+
publisher_id=self.__class__.__qualname__,
887887
)
888888

889889
def emit_response_events(self, chunk_size: int) -> None:
@@ -911,7 +911,7 @@ def emit_response_headers_complete(self) -> None:
911911
for k, v in self.response.headers.items()
912912
},
913913
},
914-
publisher_id=self.__class__.__name__,
914+
publisher_id=self.__class__.__qualname__,
915915
)
916916

917917
def emit_response_chunk_received(self, chunk_size: int) -> None:
@@ -925,7 +925,7 @@ def emit_response_chunk_received(self, chunk_size: int) -> None:
925925
'chunk_size': chunk_size,
926926
'encoded_chunk_size': chunk_size,
927927
},
928-
publisher_id=self.__class__.__name__,
928+
publisher_id=self.__class__.__qualname__,
929929
)
930930

931931
def emit_response_complete(self) -> None:
@@ -938,7 +938,7 @@ def emit_response_complete(self) -> None:
938938
event_payload={
939939
'encoded_response_size': self.response.total_size,
940940
},
941-
publisher_id=self.__class__.__name__,
941+
publisher_id=self.__class__.__qualname__,
942942
)
943943

944944
#

0 commit comments

Comments
 (0)