@@ -325,7 +325,7 @@ def split_hosts(hosts, default_port=DEFAULT_PORT):
325325
326326
327327def parse_uri (uri , default_port = DEFAULT_PORT , validate = True , warn = False ,
328- normalize = True ):
328+ normalize = True , connect_timeout = None ):
329329 """Parse and validate a MongoDB URI.
330330
331331 Returns a dict of the form::
@@ -355,6 +355,8 @@ def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False,
355355 invalid. Default: ``False``.
356356 - `normalize` (optional): If ``True``, convert names of URI options
357357 to their internally-used names. Default: ``True``.
358+ - `connect_timeout` (optional): The maximum time in milliseconds to
359+ wait for a response from the DNS server.
358360
359361 .. versionchanged:: 3.9
360362 Added the ``normalize`` parameter.
@@ -400,6 +402,25 @@ def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False,
400402 raise InvalidURI ("A '/' is required between "
401403 "the host list and any options." )
402404
405+ if path_part :
406+ if path_part [0 ] == '?' :
407+ opts = unquote_plus (path_part [1 :])
408+ else :
409+ dbase , _ , opts = map (unquote_plus , path_part .partition ('?' ))
410+ if '.' in dbase :
411+ dbase , collection = dbase .split ('.' , 1 )
412+
413+ if _BAD_DB_CHARS .search (dbase ):
414+ raise InvalidURI ('Bad database name "%s"' % dbase )
415+
416+ if opts :
417+ options .update (split_options (opts , validate , warn , normalize ))
418+
419+ if dbase is not None :
420+ dbase = unquote_plus (dbase )
421+ if collection is not None :
422+ collection = unquote_plus (collection )
423+
403424 if '@' in host_part :
404425 userinfo , _ , hosts = host_part .rpartition ('@' )
405426 user , passwd = parse_userinfo (userinfo )
@@ -424,37 +445,26 @@ def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False,
424445 raise InvalidURI (
425446 "%s URIs must not include a port number" % (SRV_SCHEME ,))
426447
427- dns_resolver = _SrvResolver (fqdn )
448+ # Use the connection timeout. connectTimeoutMS passed as a keyword
449+ # argument overrides the same option passed in the connection string.
450+ connect_timeout = connect_timeout or options .get ("connectTimeoutMS" )
451+ dns_resolver = _SrvResolver (fqdn , connect_timeout = connect_timeout )
428452 nodes = dns_resolver .get_hosts ()
429453 dns_options = dns_resolver .get_options ()
430454 if dns_options :
431- options = split_options (dns_options , validate , warn , normalize )
432- if set (options ) - _ALLOWED_TXT_OPTS :
455+ parsed_dns_options = split_options (
456+ dns_options , validate , warn , normalize )
457+ if set (parsed_dns_options ) - _ALLOWED_TXT_OPTS :
433458 raise ConfigurationError (
434459 "Only authSource and replicaSet are supported from DNS" )
435- options ["ssl" ] = True if validate else 'true'
460+ for opt , val in parsed_dns_options .items ():
461+ if opt not in options :
462+ options [opt ] = val
463+ if "ssl" not in options :
464+ options ["ssl" ] = True if validate else 'true'
436465 else :
437466 nodes = split_hosts (hosts , default_port = default_port )
438467
439- if path_part :
440- if path_part [0 ] == '?' :
441- opts = unquote_plus (path_part [1 :])
442- else :
443- dbase , _ , opts = map (unquote_plus , path_part .partition ('?' ))
444- if '.' in dbase :
445- dbase , collection = dbase .split ('.' , 1 )
446-
447- if _BAD_DB_CHARS .search (dbase ):
448- raise InvalidURI ('Bad database name "%s"' % dbase )
449-
450- if opts :
451- options .update (split_options (opts , validate , warn , normalize ))
452-
453- if dbase is not None :
454- dbase = unquote_plus (dbase )
455- if collection is not None :
456- collection = unquote_plus (collection )
457-
458468 return {
459469 'nodelist' : nodes ,
460470 'username' : user ,
0 commit comments