Skip to content

Commit af3e930

Browse files
API docs: elaborate address resolver docs (#874)
also fix CSS: vertical alignment of dl/dt/dd Co-authored-by: Stefano Ottolenghi <[email protected]>
1 parent 35da681 commit af3e930

File tree

20 files changed

+283
-109
lines changed

20 files changed

+283
-109
lines changed

docs/source/_static/nature_custom.css_t

+4
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,7 @@ li > p:first-child {
311311
li > p:last-child {
312312
margin-top: 10px;
313313
}
314+
315+
dl {
316+
align-items: baseline;
317+
}

docs/source/api.rst

+49-20
Original file line numberDiff line numberDiff line change
@@ -265,33 +265,45 @@ The maximum total number of connections allowed, per host (i.e. cluster nodes),
265265

266266
``resolver``
267267
------------
268-
A custom resolver function to resolve host and port values ahead of DNS resolution.
269-
This function is called with a 2-tuple of (host, port) and should return an iterable of 2-tuples (host, port).
268+
A custom resolver function to resolve any addresses the driver receives ahead of DNS resolution.
269+
This function is called with an :class:`.Address` and should return an iterable of :class:`.Address` objects or values that can be used to construct :class:`.Address` objects.
270270

271271
If no custom resolver function is supplied, the internal resolver moves straight to regular DNS resolution.
272272

273273
For example:
274274

275275
.. code-block:: python
276276
277-
from neo4j import GraphDatabase
277+
import neo4j
278278
279279
280-
def custom_resolver(socket_address):
281-
if socket_address == ("example.com", 9999):
282-
yield "::1", 7687
283-
yield "127.0.0.1", 7687
284-
else:
285-
from socket import gaierror
286-
raise gaierror("Unexpected socket address %r" % socket_address)
280+
def custom_resolver(socket_address):
281+
# assert isinstance(socket_address, neo4j.Address)
282+
if socket_address != ("example.com", 9999):
283+
raise OSError(f"Unexpected socket address {socket_address!r}")
287284
285+
# You can return any neo4j.Address object
286+
yield neo4j.Address(("localhost", 7687)) # IPv4
287+
yield neo4j.Address(("::1", 7687, 0, 0)) # IPv6
288+
yield neo4j.Address.parse("localhost:7687")
289+
yield neo4j.Address.parse("[::1]:7687")
288290
289-
driver = GraphDatabase.driver("neo4j://example.com:9999",
290-
auth=("neo4j", "password"),
291-
resolver=custom_resolver)
291+
# or any tuple that can be passed to neo4j.Address(...).
292+
# Initially, this will be interpreted as IPv4, but DNS resolution
293+
# will turn it into IPv6 if appropriate.
294+
yield "::1", 7687
295+
# This will be interpreted as IPv6 directly, but DNS resolution will
296+
# still happen.
297+
yield "::1", 7687, 0, 0
298+
yield "127.0.0.1", 7687
292299
293300
294-
:Default: :const:`None`
301+
driver = neo4j.GraphDatabase.driver("neo4j://example.com:9999",
302+
auth=("neo4j", "password"),
303+
resolver=custom_resolver)
304+
305+
306+
:Default: :data:`None`
295307

296308

297309
.. _trust-ref:
@@ -337,8 +349,8 @@ If given, ``encrypted`` and ``trusted_certificates`` have no effect.
337349

338350
Its usage is strongly discouraged and comes without any guarantees.
339351

340-
:Type: :class:`ssl.SSLContext` or :const:`None`
341-
:Default: :const:`None`
352+
:Type: :class:`ssl.SSLContext` or :data:`None`
353+
:Default: :data:`None`
342354

343355
.. versionadded:: 5.0
344356

@@ -656,7 +668,7 @@ context of the impersonated user. For this, the user for which the
656668

657669
:Type: ``str``, None
658670

659-
:Default: :const:`None`
671+
:Default: :data:`None`
660672

661673

662674
.. _default-access-mode-ref:
@@ -721,8 +733,8 @@ See :class:`.BookmarkManager` for more information.
721733
For simple use-cases, it often suffices that work within a single session
722734
is automatically causally consistent.
723735

724-
:Type: :const:`None` or :class:`.BookmarkManager`
725-
:Default: :const:`None`
736+
:Type: :data:`None` or :class:`.BookmarkManager`
737+
:Default: :data:`None`
726738

727739
.. versionadded:: 5.0
728740

@@ -1069,7 +1081,7 @@ The core types with their general mappings are listed below:
10691081
+------------------------+---------------------------------------------------------------------------------------------------------------------------+
10701082
| Cypher Type | Python Type |
10711083
+========================+===========================================================================================================================+
1072-
| Null | :const:`None` |
1084+
| Null | :data:`None` |
10731085
+------------------------+---------------------------------------------------------------------------------------------------------------------------+
10741086
| Boolean | :class:`bool` |
10751087
+------------------------+---------------------------------------------------------------------------------------------------------------------------+
@@ -1295,6 +1307,23 @@ BookmarkManager
12951307
:members:
12961308

12971309

1310+
*************************
1311+
Constants, Enums, Helpers
1312+
*************************
1313+
1314+
.. autoclass:: neo4j.Address
1315+
:show-inheritance:
1316+
:members:
1317+
1318+
1319+
.. autoclass:: neo4j.IPv4Address()
1320+
:show-inheritance:
1321+
1322+
1323+
.. autoclass:: neo4j.IPv6Address()
1324+
:show-inheritance:
1325+
1326+
12981327
.. _errors-ref:
12991328

13001329
******

docs/source/async_api.rst

+25-13
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,8 @@ driver accepts an async custom resolver function:
152152

153153
``resolver``
154154
------------
155-
A custom resolver function to resolve host and port values ahead of DNS resolution.
156-
This function is called with a 2-tuple of (host, port) and should return an iterable of 2-tuples (host, port).
155+
A custom resolver function to resolve any addresses the driver receives ahead of DNS resolution.
156+
This function is called with an :class:`.Address` and should return an iterable of :class:`.Address` objects or values that can be used to construct :class:`.Address` objects.
157157

158158
If no custom resolver function is supplied, the internal resolver moves straight to regular DNS resolution.
159159

@@ -163,29 +163,41 @@ For example:
163163

164164
.. code-block:: python
165165
166-
from neo4j import AsyncGraphDatabase
166+
import neo4j
167167
168168
169169
async def custom_resolver(socket_address):
170-
if socket_address == ("example.com", 9999):
171-
yield "::1", 7687
172-
yield "127.0.0.1", 7687
173-
else:
174-
from socket import gaierror
175-
raise gaierror("Unexpected socket address %r" % socket_address)
170+
# assert isinstance(socket_address, neo4j.Address)
171+
if socket_address != ("example.com", 9999):
172+
raise OSError(f"Unexpected socket address {socket_address!r}")
173+
174+
# You can return any neo4j.Address object
175+
yield neo4j.Address(("localhost", 7687)) # IPv4
176+
yield neo4j.Address(("::1", 7687, 0, 0)) # IPv6
177+
yield neo4j.Address.parse("localhost:7687")
178+
yield neo4j.Address.parse("[::1]:7687")
179+
180+
# or any tuple that can be passed to neo4j.Address(...).
181+
# Initially, this will be interpreted as IPv4, but DNS resolution
182+
# will turn it into IPv6 if appropriate.
183+
yield "::1", 7687
184+
# This will be interpreted as IPv6 directly, but DNS resolution will
185+
# still happen.
186+
yield "::1", 7687, 0, 0
187+
yield "127.0.0.1", 7687
176188
177189
178190
# alternatively
179191
def custom_resolver(socket_address):
180192
...
181193
182194
183-
driver = AsyncGraphDatabase.driver("neo4j://example.com:9999",
195+
driver = neo4j.GraphDatabase.driver("neo4j://example.com:9999",
184196
auth=("neo4j", "password"),
185197
resolver=custom_resolver)
186198
187199
188-
:Default: :const:`None`
200+
:Default: :data:`None`
189201

190202

191203

@@ -406,8 +418,8 @@ See :class:`BookmarkManager` for more information.
406418
group a series of queries together that will be causally chained
407419
automatically.
408420

409-
:Type: :const:`None`, :class:`BookmarkManager`, or :class:`AsyncBookmarkManager`
410-
:Default: :const:`None`
421+
:Type: :data:`None`, :class:`BookmarkManager`, or :class:`AsyncBookmarkManager`
422+
:Default: :data:`None`
411423

412424
**This is experimental.** (See :ref:`filter-warnings-ref`)
413425
It might be changed or removed any time even without prior notice.

src/neo4j/_async/driver.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ def bookmark_manager(
274274
Function which will be called whenever the set of bookmarks
275275
handled by the bookmark manager gets updated with the new
276276
internal bookmark set. It will receive the new set of bookmarks
277-
as a :class:`.Bookmarks` object and return :const:`None`.
277+
as a :class:`.Bookmarks` object and return :data:`None`.
278278
279279
:returns: A default implementation of :class:`AsyncBookmarkManager`.
280280

src/neo4j/_async/io/_pool.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ async def update_connection_pool(self, *, database):
679679
routing_table = await self.get_or_create_routing_table(database)
680680
servers = routing_table.servers()
681681
for address in list(self.connections):
682-
if address.unresolved not in servers:
682+
if address._unresolved not in servers:
683683
await super(AsyncNeo4jPool, self).deactivate(address)
684684

685685
async def ensure_routing_table_is_fresh(

src/neo4j/_async/work/result.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ async def single(self, strict: bool = False) -> t.Optional[Record]:
418418
there is not exactly one record left.
419419
420420
If ``strict`` is :const:`False`, fewer than one record will make this
421-
method return :const:`None`, more than one record will make this method
421+
method return :data:`None`, more than one record will make this method
422422
emit a warning and return the first record.
423423
424424
:param strict:
@@ -427,7 +427,7 @@ async def single(self, strict: bool = False) -> t.Optional[Record]:
427427
warning if there are more than 1 record.
428428
:const:`False` by default.
429429
430-
:returns: the next :class:`neo4j.Record` or :const:`None` if none remain
430+
:returns: the next :class:`neo4j.Record` or :data:`None` if none remain
431431
432432
:warns: if more than one record is available and
433433
``strict`` is :const:`False`
@@ -496,7 +496,7 @@ async def peek(self) -> t.Optional[Record]:
496496
497497
This leaves the record in the buffer for further processing.
498498
499-
:returns: the next :class:`neo4j.Record` or :const:`None` if none
499+
:returns: the next :class:`neo4j.Record` or :data:`None` if none
500500
remain.
501501
502502
:raises ResultConsumedError: if the transaction from which this result
@@ -683,7 +683,7 @@ async def to_df(
683683
:param parse_dates:
684684
If :const:`True`, columns that exclusively contain
685685
:class:`time.DateTime` objects, :class:`time.Date` objects, or
686-
:const:`None`, will be converted to :class:`pandas.Timestamp`.
686+
:data:`None`, will be converted to :class:`pandas.Timestamp`.
687687
688688
:raises ImportError: if `pandas` library is not available.
689689
:raises ResultConsumedError: if the transaction from which this result

src/neo4j/_async/work/transaction.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,10 @@ async def run(
111111
arguments, or as a mixture of both. For example, the `run`
112112
queries below are all equivalent::
113113
114-
>>> query = "CREATE (a:Person { name: $name, age: $age })"
115-
>>> result = await tx.run(query, {"name": "Alice", "age": 33})
116-
>>> result = await tx.run(query, {"name": "Alice"}, age=33)
117-
>>> result = await tx.run(query, name="Alice", age=33)
114+
query = "CREATE (a:Person { name: $name, age: $age })"
115+
result = await tx.run(query, {"name": "Alice", "age": 33})
116+
result = await tx.run(query, {"name": "Alice"}, age=33)
117+
result = await tx.run(query, name="Alice", age=33)
118118
119119
Parameter values can be of any type supported by the Neo4j type
120120
system. In Python, this includes :class:`bool`, :class:`int`,

src/neo4j/_async_compat/network/_bolt_socket.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ async def _connect_secure(cls, resolved_address, timeout, keep_alive, ssl):
207207
ssl_kwargs = {}
208208

209209
if ssl is not None:
210-
hostname = resolved_address.host_name or None
210+
hostname = resolved_address._host_name or None
211211
ssl_kwargs.update(
212212
ssl=ssl, server_hostname=hostname if HAS_SNI else None
213213
)
@@ -232,7 +232,7 @@ async def _connect_secure(cls, resolved_address, timeout, keep_alive, ssl):
232232
raise BoltProtocolError(
233233
"When using an encrypted socket, the server should "
234234
"always provide a certificate",
235-
address=(resolved_address.host_name, local_port)
235+
address=(resolved_address._host_name, local_port)
236236
)
237237

238238
return cls(reader, protocol, writer)
@@ -257,7 +257,7 @@ async def _connect_secure(cls, resolved_address, timeout, keep_alive, ssl):
257257
await cls.close_socket(s)
258258
raise BoltSecurityError(
259259
message="Failed to establish encrypted connection.",
260-
address=(resolved_address.host_name, local_port)
260+
address=(resolved_address._host_name, local_port)
261261
) from error
262262
except Exception as error:
263263
log.debug("[#0000] S: <ERROR> %s %s", type(error).__name__,
@@ -650,7 +650,7 @@ def connect(cls, address, *, timeout, custom_resolver, ssl_context,
650650
s = None
651651
try:
652652
s = BoltSocket._connect(resolved_address, timeout, keep_alive)
653-
s = BoltSocket._secure(s, resolved_address.host_name,
653+
s = BoltSocket._secure(s, resolved_address._host_name,
654654
ssl_context)
655655
return BoltSocket._handshake(s, resolved_address)
656656
except (BoltError, DriverError, OSError) as error:

src/neo4j/_async_compat/network/_util.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ async def _dns_resolver(address, family=0):
4747
)
4848
except OSError:
4949
raise ValueError("Cannot resolve address {}".format(address))
50-
return list(_resolved_addresses_from_info(info, address.host_name))
50+
return list(_resolved_addresses_from_info(info, address._host_name))
5151

5252
@staticmethod
5353
async def resolve_address(address, family=0, resolver=None):
@@ -117,7 +117,7 @@ def _dns_resolver(address, family=0):
117117
)
118118
except OSError:
119119
raise ValueError("Cannot resolve address {}".format(address))
120-
return _resolved_addresses_from_info(info, address.host_name)
120+
return _resolved_addresses_from_info(info, address._host_name)
121121

122122
@staticmethod
123123
def resolve_address(address, family=0, resolver=None):

src/neo4j/_data.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ def data(self, *keys: _K) -> t.Dict[str, t.Any]:
246246
""" Return the keys and values of this record as a dictionary,
247247
optionally including only certain values by index or key. Keys
248248
provided in the items that are not in the record will be
249-
inserted with a value of :const:`None`; indexes provided
249+
inserted with a value of :data:`None`; indexes provided
250250
that are out of bounds will trigger an :exc:`IndexError`.
251251
252252
:param keys: indexes or keys of the items to include; if none

src/neo4j/_sync/driver.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ def bookmark_manager(
271271
Function which will be called whenever the set of bookmarks
272272
handled by the bookmark manager gets updated with the new
273273
internal bookmark set. It will receive the new set of bookmarks
274-
as a :class:`.Bookmarks` object and return :const:`None`.
274+
as a :class:`.Bookmarks` object and return :data:`None`.
275275
276276
:returns: A default implementation of :class:`BookmarkManager`.
277277

src/neo4j/_sync/io/_pool.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ def update_connection_pool(self, *, database):
679679
routing_table = self.get_or_create_routing_table(database)
680680
servers = routing_table.servers()
681681
for address in list(self.connections):
682-
if address.unresolved not in servers:
682+
if address._unresolved not in servers:
683683
super(Neo4jPool, self).deactivate(address)
684684

685685
def ensure_routing_table_is_fresh(

src/neo4j/_sync/work/result.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ def single(self, strict: bool = False) -> t.Optional[Record]:
418418
there is not exactly one record left.
419419
420420
If ``strict`` is :const:`False`, fewer than one record will make this
421-
method return :const:`None`, more than one record will make this method
421+
method return :data:`None`, more than one record will make this method
422422
emit a warning and return the first record.
423423
424424
:param strict:
@@ -427,7 +427,7 @@ def single(self, strict: bool = False) -> t.Optional[Record]:
427427
warning if there are more than 1 record.
428428
:const:`False` by default.
429429
430-
:returns: the next :class:`neo4j.Record` or :const:`None` if none remain
430+
:returns: the next :class:`neo4j.Record` or :data:`None` if none remain
431431
432432
:warns: if more than one record is available and
433433
``strict`` is :const:`False`
@@ -496,7 +496,7 @@ def peek(self) -> t.Optional[Record]:
496496
497497
This leaves the record in the buffer for further processing.
498498
499-
:returns: the next :class:`neo4j.Record` or :const:`None` if none
499+
:returns: the next :class:`neo4j.Record` or :data:`None` if none
500500
remain.
501501
502502
:raises ResultConsumedError: if the transaction from which this result
@@ -683,7 +683,7 @@ def to_df(
683683
:param parse_dates:
684684
If :const:`True`, columns that exclusively contain
685685
:class:`time.DateTime` objects, :class:`time.Date` objects, or
686-
:const:`None`, will be converted to :class:`pandas.Timestamp`.
686+
:data:`None`, will be converted to :class:`pandas.Timestamp`.
687687
688688
:raises ImportError: if `pandas` library is not available.
689689
:raises ResultConsumedError: if the transaction from which this result

0 commit comments

Comments
 (0)