Skip to content

Commit 0d1fe19

Browse files
authored
DescriptorsHandlerMixin and Descriptors, SelectableEvents types (#938)
* Add `Descriptors` type * Add a `DescriptorsHandlerMixin` class used throughout the http framework * Remove dependency upon `HasFileno` ie `typing_extension` too * Define `SelectableEvents` type * Fix doc * Blank line * Remove dep on `typing-extensions` * Discover base plugin class * await on now async handlers
1 parent 7d3eee0 commit 0d1fe19

File tree

24 files changed

+152
-186
lines changed

24 files changed

+152
-186
lines changed

Makefile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ lib-clean:
9898
lib-dep:
9999
pip install --upgrade pip && \
100100
pip install \
101-
-r requirements.txt \
102101
-r requirements-testing.txt \
103102
-r requirements-release.txt \
104103
-r requirements-tunnel.txt && \

docs/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@
299299
(_py_class_role, 'proxy.core.acceptor.threadless.T'),
300300
(_py_class_role, 'proxy.core.acceptor.work.T'),
301301
(_py_class_role, 'queue.Queue[Any]'),
302+
(_py_class_role, 'SelectableEvents'),
302303
(_py_class_role, 'TcpClientConnection'),
303304
(_py_class_role, 'TcpServerConnection'),
304305
(_py_class_role, 'unittest.case.TestCase'),

examples/web_scraper.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@
1010
"""
1111
import time
1212

13-
from typing import Dict
14-
1513
from proxy import Proxy
1614
from proxy.core.acceptor import Work
1715
from proxy.core.connection import TcpClientConnection
18-
from proxy.common.types import Readables, Writables
16+
from proxy.common.types import Readables, SelectableEvents, Writables
1917

2018

2119
class WebScraper(Work[TcpClientConnection]):
@@ -40,7 +38,7 @@ class WebScraper(Work[TcpClientConnection]):
4038
only PUBSUB protocol.
4139
"""
4240

43-
async def get_events(self) -> Dict[int, int]:
41+
async def get_events(self) -> SelectableEvents:
4442
"""Return sockets and events (read or write) that we are interested in."""
4543
return {}
4644

helper/monitor_open_files.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ pgrep -P "$PROXY_PY_PID" | while read -r acceptorPid; do
2727
OPEN_FILES_BY_ACCEPTOR=$(lsof -p "$acceptorPid" | wc -l)
2828
echo "[$acceptorPid] Acceptor process: $OPEN_FILES_BY_ACCEPTOR"
2929

30-
pgrep -P "$acceptorPid" | while read -r threadlessPid; do
31-
OPEN_FILES_BY_THREADLESS=$(lsof -p "$threadlessPid" | wc -l)
32-
echo " [$threadlessPid] Threadless process: $OPEN_FILES_BY_THREADLESS"
30+
pgrep -P "$acceptorPid" | while read -r childPid; do
31+
OPEN_FILES_BY_CHILD_PROC=$(lsof -p "$childPid" | wc -l)
32+
echo " [$childPid] child process: $OPEN_FILES_BY_CHILD_PROC"
3333
done
3434
done

proxy/common/plugins.py

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
:license: BSD, see LICENSE for more details.
1010
"""
1111
import os
12-
import abc
1312
import logging
1413
import inspect
1514
import itertools
@@ -72,20 +71,19 @@ def load(
7271
klass, module_name = Plugins.importer(plugin_)
7372
assert klass and module_name
7473
mro = list(inspect.getmro(klass))
75-
mro.reverse()
76-
iterator = iter(mro)
77-
try:
78-
while next(iterator) is not abc.ABC:
79-
pass
80-
base_klass = next(iterator)
81-
if klass not in p[bytes_(base_klass.__name__)]:
82-
p[bytes_(base_klass.__name__)].append(klass)
83-
logger.info('Loaded plugin %s.%s', module_name, klass.__name__)
84-
except StopIteration:
85-
logger.warn(
86-
'%s is NOT a valid plugin',
87-
text_(plugin_),
88-
)
74+
# Find the base plugin class that
75+
# this plugin_ is implementing
76+
found = False
77+
for base_klass in mro:
78+
if bytes_(base_klass.__name__) in p:
79+
found = True
80+
break
81+
if not found:
82+
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__)
86+
# print(p)
8987
return p
9088

9189
@staticmethod

proxy/common/types.py

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,8 @@
1010
"""
1111
import queue
1212
import ipaddress
13-
import sys
1413

15-
from typing import TYPE_CHECKING, Dict, Any, List, Union
16-
17-
# NOTE: Using try/except causes linting problems which is why it's necessary
18-
# NOTE: to use this mypy/pylint idiom for py36-py38 compatibility
19-
# Ref: https://github.com/python/typeshed/issues/3500#issuecomment-560958608
20-
if sys.version_info >= (3, 8):
21-
from typing import Protocol
22-
else:
23-
from typing_extensions import Protocol
14+
from typing import TYPE_CHECKING, Dict, Any, List, Tuple, Union
2415

2516

2617
if TYPE_CHECKING:
@@ -29,11 +20,11 @@
2920
DictQueueType = queue.Queue
3021

3122

32-
class HasFileno(Protocol):
33-
def fileno(self) -> int:
34-
... # pragma: no cover
35-
23+
Selectable = int
24+
Selectables = List[Selectable]
25+
SelectableEvents = Dict[Selectable, int] # Values are event masks
26+
Readables = Selectables
27+
Writables = Selectables
28+
Descriptors = Tuple[Readables, Writables]
3629

37-
Readables = List[Union[int, HasFileno]]
38-
Writables = List[Union[int, HasFileno]]
3930
IpAddress = Union[ipaddress.IPv4Address, ipaddress.IPv6Address]

proxy/core/acceptor/threadless.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from typing import Any, Dict, Optional, Tuple, List, Set, Generic, TypeVar, Union
2222

2323
from ...common.logger import Logger
24-
from ...common.types import Readables, Writables
24+
from ...common.types import Readables, SelectableEvents, Writables
2525
from ...common.constants import DEFAULT_INACTIVE_CONN_CLEANUP_TIMEOUT, DEFAULT_SELECTOR_SELECT_TIMEOUT
2626
from ...common.constants import DEFAULT_WAIT_FOR_TASKS_TIMEOUT
2727

@@ -82,7 +82,7 @@ def __init__(
8282
# work_id
8383
int,
8484
# fileno, mask
85-
Dict[int, int],
85+
SelectableEvents,
8686
] = {}
8787
self.wait_timeout: float = DEFAULT_WAIT_FOR_TASKS_TIMEOUT
8888
self.cleanup_inactive_timeout: float = DEFAULT_INACTIVE_CONN_CLEANUP_TIMEOUT
@@ -288,9 +288,9 @@ async def _selected_events(self) -> Tuple[
288288
if key.data not in work_by_ids:
289289
work_by_ids[key.data] = ([], [])
290290
if mask & selectors.EVENT_READ:
291-
work_by_ids[key.data][0].append(key.fileobj)
291+
work_by_ids[key.data][0].append(key.fd)
292292
if mask & selectors.EVENT_WRITE:
293-
work_by_ids[key.data][1].append(key.fileobj)
293+
work_by_ids[key.data][1].append(key.fd)
294294
return (work_by_ids, new_work_available)
295295

296296
async def _wait_for_tasks(self) -> Set['asyncio.Task[bool]']:

proxy/core/acceptor/work.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
"""
1515
import argparse
1616

17-
from abc import ABC, abstractmethod
1817
from uuid import uuid4
18+
from abc import ABC, abstractmethod
1919
from typing import Optional, Dict, Any, TypeVar, Generic, TYPE_CHECKING
2020

2121
from ..event import eventNames, EventQueue
22-
from ...common.types import Readables, Writables
22+
from ...common.types import Readables, SelectableEvents, Writables
2323

2424
if TYPE_CHECKING:
2525
from ..connection import UpstreamConnectionPool
@@ -48,7 +48,7 @@ def __init__(
4848
self.upstream_conn_pool = upstream_conn_pool
4949

5050
@abstractmethod
51-
async def get_events(self) -> Dict[int, int]:
51+
async def get_events(self) -> SelectableEvents:
5252
"""Return sockets and events (read or write) that we are interested in."""
5353
return {} # pragma: no cover
5454

proxy/core/base/tcp_server.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
import selectors
1717

1818
from abc import abstractmethod
19-
from typing import Dict, Any, Optional
19+
from typing import Any, Optional
2020

2121
from ...core.acceptor import Work
2222
from ...core.connection import TcpClientConnection
23-
from ...common.types import Readables, Writables
23+
from ...common.types import Readables, SelectableEvents, Writables
2424

2525
logger = logging.getLogger(__name__)
2626

@@ -61,7 +61,7 @@ def handle_data(self, data: memoryview) -> Optional[bool]:
6161
"""Optionally return True to close client connection."""
6262
pass # pragma: no cover
6363

64-
async def get_events(self) -> Dict[int, int]:
64+
async def get_events(self) -> SelectableEvents:
6565
events = {}
6666
# We always want to read from client
6767
# Register for EVENT_READ events

proxy/core/base/tcp_tunnel.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212
import selectors
1313

1414
from abc import abstractmethod
15-
from typing import Any, Optional, Dict
15+
from typing import Any, Optional
1616

1717
from ...http.parser import HttpParser, httpParserTypes
18-
from ...common.types import Readables, Writables
18+
from ...common.types import Readables, SelectableEvents, Writables
1919
from ...common.utils import text_
2020

2121
from ..connection import TcpServerConnection
@@ -60,9 +60,9 @@ def shutdown(self) -> None:
6060
self.upstream.close()
6161
super().shutdown()
6262

63-
async def get_events(self) -> Dict[int, int]:
63+
async def get_events(self) -> SelectableEvents:
6464
# Get default client events
65-
ev: Dict[int, int] = await super().get_events()
65+
ev: SelectableEvents = await super().get_events()
6666
# Read from server if we are connected
6767
if self.upstream and self.upstream._conn is not None:
6868
ev[self.upstream.connection.fileno()] = selectors.EVENT_READ

0 commit comments

Comments
 (0)