@@ -165,7 +165,7 @@ def _validate_port_spec(hosts, port):
165165 # If there is a list of ports, its length must
166166 # match that of the host list.
167167 if len (port ) != len (hosts ):
168- raise exceptions .InterfaceError (
168+ raise exceptions .ClientConfigurationError (
169169 'could not match {} port numbers to {} hosts' .format (
170170 len (port ), len (hosts )))
171171 else :
@@ -211,7 +211,7 @@ def _parse_hostlist(hostlist, port, *, unquote=False):
211211 addr = m .group (1 )
212212 hostspec_port = m .group (2 )
213213 else :
214- raise ValueError (
214+ raise exceptions . ClientConfigurationError (
215215 'invalid IPv6 address in the connection URI: {!r}' .format (
216216 hostspec
217217 )
@@ -240,13 +240,13 @@ def _parse_hostlist(hostlist, port, *, unquote=False):
240240
241241def _parse_tls_version (tls_version ):
242242 if tls_version .startswith ('SSL' ):
243- raise ValueError (
243+ raise exceptions . ClientConfigurationError (
244244 f"Unsupported TLS version: { tls_version } "
245245 )
246246 try :
247247 return ssl_module .TLSVersion [tls_version .replace ('.' , '_' )]
248248 except KeyError :
249- raise ValueError (
249+ raise exceptions . ClientConfigurationError (
250250 f"No such TLS version: { tls_version } "
251251 )
252252
@@ -274,7 +274,7 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
274274 parsed = urllib .parse .urlparse (dsn )
275275
276276 if parsed .scheme not in {'postgresql' , 'postgres' }:
277- raise ValueError (
277+ raise exceptions . ClientConfigurationError (
278278 'invalid DSN: scheme is expected to be either '
279279 '"postgresql" or "postgres", got {!r}' .format (parsed .scheme ))
280280
@@ -437,11 +437,11 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
437437 database = user
438438
439439 if user is None :
440- raise exceptions .InterfaceError (
440+ raise exceptions .ClientConfigurationError (
441441 'could not determine user name to connect with' )
442442
443443 if database is None :
444- raise exceptions .InterfaceError (
444+ raise exceptions .ClientConfigurationError (
445445 'could not determine database name to connect to' )
446446
447447 if password is None :
@@ -477,7 +477,7 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
477477 have_tcp_addrs = True
478478
479479 if not addrs :
480- raise ValueError (
480+ raise exceptions . InternalClientError (
481481 'could not determine the database address to connect to' )
482482
483483 if ssl is None :
@@ -491,7 +491,7 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
491491 sslmode = SSLMode .parse (ssl )
492492 except AttributeError :
493493 modes = ', ' .join (m .name .replace ('_' , '-' ) for m in SSLMode )
494- raise exceptions .InterfaceError (
494+ raise exceptions .ClientConfigurationError (
495495 '`sslmode` parameter must be one of: {}' .format (modes ))
496496
497497 # docs at https://www.postgresql.org/docs/10/static/libpq-connect.html
@@ -511,19 +511,36 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
511511 else :
512512 try :
513513 sslrootcert = _dot_postgresql_path ('root.crt' )
514- assert sslrootcert is not None
515- ssl .load_verify_locations (cafile = sslrootcert )
516- except (AssertionError , FileNotFoundError ):
514+ if sslrootcert is not None :
515+ ssl .load_verify_locations (cafile = sslrootcert )
516+ else :
517+ raise exceptions .ClientConfigurationError (
518+ 'cannot determine location of user '
519+ 'PostgreSQL configuration directory'
520+ )
521+ except (
522+ exceptions .ClientConfigurationError ,
523+ FileNotFoundError ,
524+ NotADirectoryError ,
525+ ):
517526 if sslmode > SSLMode .require :
518527 if sslrootcert is None :
519- raise RuntimeError (
520- 'Cannot determine home directory'
528+ sslrootcert = '~/.postgresql/root.crt'
529+ detail = (
530+ 'Could not determine location of user '
531+ 'home directory (HOME is either unset, '
532+ 'inaccessible, or does not point to a '
533+ 'valid directory)'
521534 )
522- raise ValueError (
535+ else :
536+ detail = None
537+ raise exceptions .ClientConfigurationError (
523538 f'root certificate file "{ sslrootcert } " does '
524- f'not exist\n Either provide the file or '
525- f'change sslmode to disable server '
526- f'certificate verification.'
539+ f'not exist or cannot be accessed' ,
540+ hint = 'Provide the certificate file directly '
541+ f'or make sure "{ sslrootcert } " '
542+ 'exists and is readable.' ,
543+ detail = detail ,
527544 )
528545 elif sslmode == SSLMode .require :
529546 ssl .verify_mode = ssl_module .CERT_NONE
@@ -542,7 +559,10 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
542559 if sslcrl is not None :
543560 try :
544561 ssl .load_verify_locations (cafile = sslcrl )
545- except FileNotFoundError :
562+ except (
563+ FileNotFoundError ,
564+ NotADirectoryError ,
565+ ):
546566 pass
547567 else :
548568 ssl .verify_flags |= \
@@ -571,7 +591,7 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
571591 keyfile = sslkey ,
572592 password = lambda : sslpassword
573593 )
574- except FileNotFoundError :
594+ except ( FileNotFoundError , NotADirectoryError ) :
575595 pass
576596
577597 # OpenSSL 1.1.1 keylog file, copied from create_default_context()
@@ -606,7 +626,7 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
606626 not isinstance (server_settings , dict ) or
607627 not all (isinstance (k , str ) for k in server_settings ) or
608628 not all (isinstance (v , str ) for v in server_settings .values ())):
609- raise ValueError (
629+ raise exceptions . ClientConfigurationError (
610630 'server_settings is expected to be None or '
611631 'a Dict[str, str]' )
612632
@@ -617,7 +637,7 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
617637 try :
618638 target_session_attrs = SessionAttribute (target_session_attrs )
619639 except ValueError :
620- raise exceptions .InterfaceError (
640+ raise exceptions .ClientConfigurationError (
621641 "target_session_attrs is expected to be one of "
622642 "{!r}"
623643 ", got {!r}" .format (
0 commit comments