Skip to content

Commit bff1f98

Browse files
authored
Merge pull request #8 from dapper91/dev
- refactoring done
2 parents 83c2c3c + f556bc7 commit bff1f98

Some content is hidden

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

46 files changed

+3286
-1695
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,5 +128,8 @@ dmypy.json
128128
# Pyre type checker
129129
.pyre/
130130

131+
# IDE
132+
.idea
133+
131134
# poetry
132135
poetry.lock

.readthedocs.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
2+
3+
version: 2
4+
5+
build:
6+
os: ubuntu-22.04
7+
tools:
8+
python: "3.11"
9+
10+
sphinx:
11+
configuration: docs/source/conf.py
12+
13+
python:
14+
install:
15+
- method: pip
16+
path: .
17+
extra_requirements:
18+
- docs

CHANGELOG.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
Changelog
22
=========
33

4+
0.3.0 (2023-08-10)
5+
------------------
6+
7+
- refactoring done.
8+
9+
410
0.2.0 (2023-04-19)
511
------------------
612

README.rst

Lines changed: 58 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -17,218 +17,96 @@ generic-connection-pool
1717
.. image:: https://codecov.io/gh/dapper91/generic-connection-pool/branch/master/graph/badge.svg
1818
:target: https://codecov.io/gh/dapper91/generic-connection-pool
1919
:alt: Code coverage
20+
.. image:: https://readthedocs.org/projects/generic-connection-pool/badge/?version=stable&style=flat
21+
:alt: ReadTheDocs status
22+
:target: https://generic-connection-pool.readthedocs.io/en/stable/
2023

2124

2225
``generic-connection-pool`` is a connection pool that can be used for TCP, http, database connections.
2326

24-
Features:
27+
Features
28+
--------
2529

2630
- **generic nature**: can be used for any connection you desire (TCP, http, database)
2731
- **runtime agnostic**: synchronous and asynchronous pool supported
2832
- **flexibility**: flexable connection retention and recycling policy
2933
- **fully-typed**: mypy type-checker compatible
3034

3135

32-
Installation
33-
------------
36+
Getting started
37+
---------------
3438

35-
You can install generic-connection-pool with pip:
39+
Connection pool supports the following configurations:
3640

37-
.. code-block:: console
41+
* **background_collector**: if ``True`` starts a background worker that disposes expired and idle connections
42+
maintaining requested pool state. If ``False`` the connections will be disposed on each connection release.
43+
* **dispose_batch_size**: maximum number of expired and idle connections to be disposed on connection release
44+
(if background collector is started the parameter is ignored).
45+
* **idle_timeout**: inactivity time (in seconds) after which an extra connection will be disposed
46+
(a connection considered as extra if the number of endpoint connection exceeds ``min_idle``).
47+
* **max_lifetime**: number of seconds after which any connection will be disposed.
48+
* **min_idle**: minimum number of connections in each endpoint the pool tries to hold. Connections that exceed
49+
that number will be considered as extra and disposed after ``idle_timeout`` seconds of inactivity.
50+
* **max_size**: maximum number of endpoint connections.
51+
* **total_max_size**: maximum number of all connections in the pool.
3852

39-
$ pip install generic-connection-pool
40-
41-
42-
Quickstart
43-
----------
44-
45-
The following example illustrates how to create asynchronous ssl socket pool:
53+
The following example illustrates how to create https pool:
4654

4755
.. code-block:: python
4856
49-
import asyncio
57+
import socket
58+
import ssl
59+
import urllib.parse
60+
from http.client import HTTPResponse
5061
from typing import Tuple
5162
52-
from generic_connection_pool.asyncio import ConnectionPool
53-
from generic_connection_pool.contrib.socket_async import TcpStreamConnectionManager
63+
from generic_connection_pool.contrib.socket import SslSocketConnectionManager
64+
from generic_connection_pool.threading import ConnectionPool
5465
5566
Hostname = str
5667
Port = int
5768
Endpoint = Tuple[Hostname, Port]
58-
Connection = Tuple[asyncio.StreamReader, asyncio.StreamWriter]
59-
60-
61-
async def main() -> None:
62-
pool = ConnectionPool[Endpoint, Connection](
63-
TcpStreamConnectionManager(ssl=True),
64-
idle_timeout=30.0,
65-
max_lifetime=600.0,
66-
min_idle=3,
67-
max_size=20,
68-
total_max_size=100,
69-
background_collector=True,
70-
)
71-
72-
async with pool.connection(endpoint=('www.wikipedia.org', 443), timeout=5.0) as (reader, writer):
73-
request = (
74-
'GET / HTTP/1.0\n'
75-
'Host: www.wikipedia.org\n'
76-
'\n'
77-
'\n'
78-
)
79-
writer.write(request.encode())
80-
await writer.drain()
81-
response = await reader.read()
82-
83-
print(response.decode())
84-
85-
asyncio.run(main())
86-
87-
88-
Configuration
89-
-------------
90-
91-
Synchronous and asynchronous pools supports the following parameters:
92-
93-
* **connection_manager**: connection manager instance
94-
* **acquire_timeout**: connection acquiring default timeout
95-
* **dispose_batch_size**: number of connections to be disposed at once
96-
(if background collector is started the parameter is ignored)
97-
* **dispose_timeout**: connection disposal timeout
98-
* **background_collector**: start worker that disposes timed-out connections in background maintain provided pool state
99-
otherwise they will be disposed on each connection release
100-
* **idle_timeout**: number of seconds after which a connection will be closed respecting min_idle parameter
101-
(the connection will be closed only if the connection number exceeds min_idle)
102-
* **max_lifetime**: number of seconds after which a connection will be closed (min_idle parameter will be ignored)
103-
* **min_idle**: minimum number of connections the pool tries to hold (for each endpoint)
104-
* **max_size**: maximum number of connections (for each endpoint)
105-
* **total_max_size**: maximum number of connections (for all endpoints)
106-
107-
108-
Generic nature
109-
--------------
110-
111-
Since the pool has generic nature is can be used for database connections as well:
112-
113-
.. code-block:: python
114-
115-
import psycopg2.extensions
116-
117-
from generic_connection_pool.contrib.psycopg2 import DbConnectionManager
118-
from generic_connection_pool.threding import ConnectionPool
119-
120-
Endpoint = str
121-
Connection = psycopg2.extensions.connection
122-
123-
124-
def main() -> None:
125-
dsn_params = dict(dbname='postgres', user='postgres', password='secret')
69+
Connection = socket.socket
12670
127-
pool = ConnectionPool[Endpoint, Connection](
128-
DbConnectionManager(
129-
dsn_params={
130-
'master': dict(dsn_params, host='db-master.local'),
131-
'replica-1': dict(dsn_params, host='db-replica-1.local'),
132-
'replica-2': dict(dsn_params, host='db-replica-2.local'),
133-
},
134-
),
135-
acquire_timeout=2.0,
136-
idle_timeout=60.0,
137-
max_lifetime=600.0,
138-
min_idle=3,
139-
max_size=10,
140-
total_max_size=15,
141-
background_collector=True,
142-
)
14371
144-
with pool.connection(endpoint='master') as conn:
145-
cur = conn.cursor()
146-
cur.execute("SELECT * FROM pg_stats;")
147-
print(cur.fetchone())
72+
http_pool = ConnectionPool[Endpoint, Connection](
73+
SslSocketConnectionManager(ssl.create_default_context()),
74+
idle_timeout=30.0,
75+
max_lifetime=600.0,
76+
min_idle=3,
77+
max_size=20,
78+
total_max_size=100,
79+
background_collector=True,
80+
)
14881
149-
with pool.connection(endpoint='replica-1') as conn:
150-
cur = conn.cursor()
151-
cur.execute("SELECT * FROM pg_stats;")
152-
print(cur.fetchone())
15382
154-
pool.close()
83+
def fetch(url: str, timeout: float = 5.0) -> None:
84+
url = urllib.parse.urlsplit(url)
85+
port = url.port or 443 if url.scheme == 'https' else 80
15586
87+
with http_pool.connection(endpoint=(url.hostname, port), timeout=timeout) as sock:
88+
request = (
89+
'GET {path} HTTP/1.1\r\n'
90+
'Host: {host}\r\n'
91+
'\r\n'
92+
'\r\n'
93+
).format(host=url.hostname, path=url.path)
15694
157-
main()
158-
159-
160-
Extendability
161-
-------------
162-
163-
If built-in connection managers are not suitable for your task the one can be easily created by yourself:
164-
165-
.. code-block:: python
166-
167-
import socket
168-
from ssl import SSLContext, SSLSocket
169-
from typing import Optional, Tuple
170-
171-
from generic_connection_pool.threding import BaseConnectionManager, ConnectionPool
172-
173-
Hostname = str
174-
Port = int
175-
SslEndpoint = Tuple[Hostname, Port]
176-
Connection = SSLSocket
177-
178-
179-
class SslSocketConnectionManager(BaseConnectionManager[SslEndpoint, Connection]):
180-
"""
181-
SSL socket connection manager.
182-
"""
183-
184-
def __init__(self, ssl: SSLContext):
185-
self._ssl = ssl
186-
187-
def create(self, endpoint: SslEndpoint, timeout: Optional[float] = None) -> Connection:
188-
hostname, port = endpoint
189-
190-
sock = self._ssl.wrap_socket(socket.socket(type=socket.SOCK_STREAM), server_hostname=hostname)
191-
sock.settimeout(timeout)
192-
sock.connect((hostname, port))
193-
194-
return sock
195-
196-
def dispose(self, endpoint: SslEndpoint, conn: Connection, timeout: Optional[float] = None) -> None:
197-
conn.settimeout(timeout)
198-
try:
199-
conn.shutdown(socket.SHUT_RDWR)
200-
except OSError:
201-
pass
202-
203-
conn.close()
204-
95+
sock.write(request.encode())
20596
206-
def main() -> None:
207-
pool = ConnectionPool[SslEndpoint, Connection](
208-
SslSocketConnectionManager(ssl=SSLContext()),
209-
idle_timeout=30.0,
210-
max_lifetime=600.0,
211-
min_idle=3,
212-
max_size=20,
213-
total_max_size=100,
214-
background_collector=True,
215-
)
97+
response = HTTPResponse(sock)
98+
response.begin()
99+
status, body = response.getcode(), response.read(response.length)
216100
217-
with pool.connection(endpoint=('www.wikipedia.org', 443), timeout=5.0) as sock:
218-
request = (
219-
'GET / HTTP/1.0\n'
220-
'Host: www.wikipedia.org\n'
221-
'\n'
222-
'\n'
223-
)
224-
sock.write(request.encode())
225-
response = []
226-
while chunk := sock.recv():
227-
response.append(chunk)
101+
print(status)
102+
print(body)
228103
229-
print(b''.join(response).decode())
230104
231-
pool.close()
105+
try:
106+
fetch('https://en.wikipedia.org/wiki/HTTP') # http connection is opened
107+
fetch('https://en.wikipedia.org/wiki/Python_(programming_language)') # http connection is reused
108+
finally:
109+
http_pool.close()
232110
233111
234-
main()
112+
See `documentation <https://generic-connection-pool.readthedocs.io/en/latest/>`_ for more details.

docs/Makefile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Minimal makefile for Sphinx documentation
2+
#
3+
4+
# You can set these variables from the command line, and also
5+
# from the environment for the first two.
6+
SPHINXOPTS ?=
7+
SPHINXBUILD ?= sphinx-build
8+
SOURCEDIR = source
9+
BUILDDIR = build
10+
11+
# Put it first so that "make" without argument is like "make help".
12+
help:
13+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14+
15+
.PHONY: help Makefile
16+
17+
# Catch-all target: route all unknown targets to Sphinx using the new
18+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19+
%: Makefile
20+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

docs/make.bat

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
@ECHO OFF
2+
3+
pushd %~dp0
4+
5+
REM Command file for Sphinx documentation
6+
7+
if "%SPHINXBUILD%" == "" (
8+
set SPHINXBUILD=sphinx-build
9+
)
10+
set SOURCEDIR=source
11+
set BUILDDIR=build
12+
13+
%SPHINXBUILD% >NUL 2>NUL
14+
if errorlevel 9009 (
15+
echo.
16+
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
17+
echo.installed, then set the SPHINXBUILD environment variable to point
18+
echo.to the full path of the 'sphinx-build' executable. Alternatively you
19+
echo.may add the Sphinx directory to PATH.
20+
echo.
21+
echo.If you don't have Sphinx installed, grab it from
22+
echo.https://www.sphinx-doc.org/
23+
exit /b 1
24+
)
25+
26+
if "%1" == "" goto help
27+
28+
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29+
goto end
30+
31+
:help
32+
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33+
34+
:end
35+
popd

0 commit comments

Comments
 (0)