Skip to content

Commit dd471bd

Browse files
author
James William Pye
committed
Improve clientparameters documentation.
* Rename 'standard' to 'collect' as it's actually descriptive. * Include a fix for the 'parameters' argument. s/.extend/.append/ * Other miscellaneous editorializing.
1 parent a71759a commit dd471bd

6 files changed

Lines changed: 321 additions & 123 deletions

File tree

postgresql/__init__.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,29 @@
3636
str(x) for x in version_info[:2]
3737
]) + '/'
3838

39-
pg_iri = pg_driver = pg_cp = None
39+
pg_iri = pg_driver = pg_param = None
4040
def open(iri = None):
4141
"""
4242
Create a `postgresql.api.Connection` to the server referenced by the given
43-
`iri` keyword.
43+
`iri` keyword::
4444
45-
If the URL starts with an '&', return the connector.
45+
>>> import postgresql
46+
# General Format:
47+
>>> db = postgresql.open('pq://user:password@host:port/database')
48+
49+
# Connect to 'postgres' at localhost.
50+
>>> db = postgresql.open('localhost/postgres')
51+
52+
If the URL starts with an '&', a connector will be returned instead of a
53+
connection.
54+
55+
(Note: "pq" is the name of the protocol used to communicate with PostgreSQL)
4656
"""
47-
global pg_iri, pg_driver, pg_cp
48-
if None in (pg_iri, pg_driver, pg_cp):
57+
global pg_iri, pg_driver, pg_param
58+
if None in (pg_iri, pg_driver, pg_param):
4959
import postgresql.iri as pg_iri
5060
import postgresql.driver as pg_driver
51-
import postgresql.clientparameters as pg_cp
61+
import postgresql.clientparameters as pg_param
5262

5363
return_connector = False
5464
if iri is not None:
@@ -60,12 +70,13 @@ def open(iri = None):
6070
else:
6171
iri_params = {}
6272

63-
std_params = pg_cp.standard(prompt_title = None)
64-
params = pg_cp.normalize(
65-
list(pg_cp.denormalize_parameters(std_params)) + \
66-
list(pg_cp.denormalize_parameters(iri_params))
73+
std_params = pg_param.collect(prompt_title = None)
74+
params = pg_param.normalize(
75+
list(pg_param.denormalize_parameters(std_params)) + \
76+
list(pg_param.denormalize_parameters(iri_params))
6777
)
68-
pg_cp.resolve_password(params)
78+
# Resolve the password, but never prompt.
79+
pg_param.resolve_password(params, prompt_title = None)
6980

7081
if return_connector is True:
7182
Ctype = pg_driver.default.select(

postgresql/bin/pg_python.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# http://python.projects.postgresql.org
44
##
55
"""
6-
Python command with a PG-API connection.
6+
Python command with a PG-API connection(``db``).
77
"""
88
import os
99
import sys
@@ -43,9 +43,9 @@ def command(argv = sys.argv):
4343
connection = None
4444
while connection is None:
4545
try:
46-
cond = clientparameters.standard(co = co, prompt_title = None)
46+
cond = clientparameters.collect(parsed_options = co, prompt_title = None)
4747
if need_prompt:
48-
# authspec error thrown last time
48+
# authspec error thrown last time, so force prompt.
4949
cond['prompt_password'] = True
5050
try:
5151
clientparameters.resolve_password(cond, prompt_title = 'pg_python')

postgresql/clientparameters.py

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,27 @@
88
This module provides functions for collecting client parameters from various
99
sources such as user relative defaults, environment variables, and even command
1010
line options.
11+
12+
There are two primary data-structures that this module deals with: normalized
13+
parameters and denormalized parameters.
14+
15+
Normalized parameters is a proper mapping object, dictionary, consisting of
16+
the parameters used to apply to a connection creation interface. The high-level
17+
interface, ``collect`` returns normalized parameters.
18+
19+
Denormalized parameters is a sequence or iterable of key-value pairs. However,
20+
the key is always a tuple whose components make up the "key-path". This is used
21+
to support sub-dictionaries like settings::
22+
23+
>>> normal_params = {
24+
'user' : 'jwp',
25+
'host' : 'localhost',
26+
'settings' : {'default_statistics_target' : 200, 'search_path' : 'home,public'}
27+
}
28+
29+
Denormalized parameters are used to simplify the overriding of past parameters.
30+
For this to work with dictionaries in a general fashion, dictionary objects
31+
would need a "deep update" method.
1132
"""
1233
import sys
1334
import os
@@ -182,7 +203,8 @@ def append_db_client_parameters(option, opt_str, value, parser):
182203
((option.dest,), value)
183204
)
184205

185-
make_option = partial(optparse.make_option,
206+
make_option = partial(
207+
optparse.make_option,
186208
action = 'callback',
187209
callback = append_db_client_parameters
188210
)
@@ -251,7 +273,7 @@ def append_db_client_x_parameters(option, opt_str, value, parser):
251273
make_x_option = partial(make_option, callback = append_db_client_x_parameters)
252274

253275
option_iri = make_x_option('-I', '--iri',
254-
help = 'database locator string [pq://user:password@host:port/database?setting=value]',
276+
help = 'database locator string [pq://user:password@host:port/database?[driver_param]=valuesetting=value]',
255277
type = 'str',
256278
dest = 'pq_iri',
257279
)
@@ -378,9 +400,7 @@ def x_pg_service(service_name, config):
378400
"""
379401
Lookup service data using the `service_name`.
380402
381-
A service file is very close to the format supported by
382-
`configparser.RawConfigParser`, so if more dynamic access is need, just use
383-
it directly. But be sure to map 'dbname' to 'database'.
403+
Be sure to map 'dbname' to 'database'.
384404
"""
385405
service_file = config.get('pg_service_file')
386406
if service_file is None:
@@ -405,6 +425,8 @@ def x_pg_service(service_name, config):
405425
elif k.lower() == 'pg_service':
406426
# ignore
407427
pass
428+
elif k.lower() == 'dbname':
429+
yield (('database',), v)
408430
else:
409431
yield ((k,), v)
410432

@@ -420,7 +442,11 @@ def x_pg_ldap(ldap_url, config):
420442

421443
def extrapolate(iter, config = None, callbacks = default_x_callbacks):
422444
"""
423-
Given an iterable of standardized
445+
Given an iterable of standardized settings,
446+
447+
[((path0, path1, ..., pathN), value)]
448+
449+
Process any callbacks.
424450
"""
425451
config = config or {}
426452
for item in iter:
@@ -479,15 +505,15 @@ def resolve_pg_service_file(
479505
return os.path.join(sysconfdir, default_pg_service_filename)
480506
return None
481507

482-
def standard(
483-
co : "options parsed using the `DefaultParser`" = None,
508+
def collect(
509+
parsed_options : "options parsed using the `DefaultParser`" = None,
484510
no_defaults : "Don't build-out defaults like 'user' from getpass.getuser()" = False,
485511
environ : "environment variables to use, `None` to disable" = os.environ,
486512
environ_prefix : "prefix to use for collecting environment variables" = 'PG',
487513
default_pg_sysconfdir : "default 'PGSYSCONFDIR' to use" = None,
488514
pg_service_file : "the pg-service file to actually use" = None,
489515
prompt_title : "additional title to use if a prompt request is made" = '',
490-
parameters : "base-client parameters to use(layers on top of defaults)" = (),
516+
parameters : "base-client parameters to use(applied after defaults)" = (),
491517
):
492518
"""
493519
Build a normalized client parameters dictionary for use with a connection
@@ -506,16 +532,19 @@ def standard(
506532

507533
if not no_defaults:
508534
d_parameters.append(defaults(environ = environ))
509-
d_parameters.extend(denormalize_parameters(dict(parameters)))
535+
536+
if parameters:
537+
d_parameters.append(denormalize_parameters(dict(parameters)))
510538

511539
if environ is not None:
512540
d_parameters.append(envvars(
513541
environ = environ,
514542
modifier = environ_prefix.__add__
515543
))
516-
cop = getattr(co, 'db_client_parameters', None)
544+
cop = getattr(parsed_options, 'db_client_parameters', None)
517545
if cop:
518546
d_parameters.append(cop)
547+
519548
cpd = normalize(extrapolate(chain(*d_parameters)))
520549
if prompt_title is not None:
521550
resolve_password(cpd, prompt_title = prompt_title)
@@ -527,5 +556,5 @@ def standard(
527556
description = "print the clientparams dictionary for the environment"
528557
)
529558
(co, ca) = p.parse_args()
530-
r = standard(co = co, prompt_title = 'custom_prompt_title')
559+
r = collect(parsed_options = co, prompt_title = 'custom_prompt_title')
531560
pprint.pprint(r)

0 commit comments

Comments
 (0)