Skip to content

Commit dde378e

Browse files
ltalirzsphuber
authored andcommitted
Backport postgres improvements to deal with setups without sudo (#2433)
Also updated `yapf` to recent version
1 parent d8f8ff1 commit dde378e

File tree

7 files changed

+118
-80
lines changed

7 files changed

+118
-80
lines changed

.prospector.yaml

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1+
max-line-length: 120
2+
13
ignore-paths:
24
- doc
35
- examples
46
- test
57
- utils
68

7-
pylint:
8-
max-line-length: 140
9-
109
pyflakes:
1110
run: false
1211

12+
pep8:
13+
run: false
14+
1315
mccabe:
1416
run: false

aiida/backends/tests/verdi_commands.py

+44-35
Original file line numberDiff line numberDiff line change
@@ -125,23 +125,27 @@ def test_calculation_list(self):
125125
calc_cmd.calculation_list()
126126

127127
out_str = ''.join(output)
128-
self.assertTrue(calc_states.TOSUBMIT in out_str,
129-
"The TOSUBMIT calculations should be part fo the "
130-
"simple calculation list.")
131-
self.assertTrue(calc_states.COMPUTED in out_str,
132-
"The COMPUTED calculations should be part fo the "
133-
"simple calculation list.")
134-
self.assertFalse(calc_states.FINISHED in out_str,
135-
"The FINISHED calculations should not be part fo the "
136-
"simple calculation list.")
128+
self.assertTrue(
129+
calc_states.TOSUBMIT in out_str,
130+
"The TOSUBMIT calculations should be part fo the "
131+
"simple calculation list.")
132+
self.assertTrue(
133+
calc_states.COMPUTED in out_str,
134+
"The COMPUTED calculations should be part fo the "
135+
"simple calculation list.")
136+
self.assertFalse(
137+
calc_states.FINISHED in out_str,
138+
"The FINISHED calculations should not be part fo the "
139+
"simple calculation list.")
137140

138141
with Capturing() as output:
139142
calc_cmd.calculation_list(*['-a'])
140143

141144
out_str = ''.join(output)
142-
self.assertTrue(calc_states.FINISHED in out_str,
143-
"The FINISHED calculations should be part fo the "
144-
"simple calculation list.")
145+
self.assertTrue(
146+
calc_states.FINISHED in out_str,
147+
"The FINISHED calculations should be part fo the "
148+
"simple calculation list.")
145149

146150

147151
class TestVerdiCodeCommands(AiidaTestCase):
@@ -193,12 +197,14 @@ def test_code_list(self):
193197
with Capturing() as output:
194198
code_cmd.code_list()
195199
out_str_1 = ''.join(output)
196-
self.assertTrue(computer_name_1 in out_str_1,
197-
"The computer 1 name should be included into this list")
200+
self.assertTrue(
201+
computer_name_1 in out_str_1,
202+
"The computer 1 name should be included into this list")
198203
self.assertTrue(code_name_1 in out_str_1,
199204
"The code 1 name should be included into this list")
200-
self.assertTrue(computer_name_2 in out_str_1,
201-
"The computer 2 name should be included into this list")
205+
self.assertTrue(
206+
computer_name_2 in out_str_1,
207+
"The computer 2 name should be included into this list")
202208
self.assertTrue(code_name_2 in out_str_1,
203209
"The code 2 name should be included into this list")
204210

@@ -207,16 +213,18 @@ def test_code_list(self):
207213
with Capturing() as output:
208214
code_cmd.code_list(*['-a'])
209215
out_str_2 = ''.join(output)
210-
self.assertEqual(out_str_1, out_str_2,
211-
"verdi code list & verdi code list -a should provide "
212-
"the same output in this experiment.")
216+
self.assertEqual(
217+
out_str_1, out_str_2,
218+
"verdi code list & verdi code list -a should provide "
219+
"the same output in this experiment.")
213220

214221
# Run a verdi code list -c, capture the output and check the result
215222
with Capturing() as output:
216223
code_cmd.code_list(*['-c', computer_name_1])
217224
out_str = ''.join(output)
218-
self.assertTrue(computer_name_1 in out_str,
219-
"The computer 1 name should be included into this list")
225+
self.assertTrue(
226+
computer_name_1 in out_str,
227+
"The computer 1 name should be included into this list")
220228
self.assertFalse(
221229
computer_name_2 in out_str,
222230
"The computer 2 name should not be included into this list")
@@ -228,8 +236,9 @@ def test_code_list(self):
228236
with Capturing() as output:
229237
code_cmd.code_list()
230238
out_str_3 = ''.join(output)
231-
self.assertTrue(computer_name_1 in out_str_3,
232-
"The computer 1 name should be included into this list")
239+
self.assertTrue(
240+
computer_name_1 in out_str_3,
241+
"The computer 1 name should be included into this list")
233242
self.assertTrue(code_name_1 in out_str_3,
234243
"The code 1 name should be included into this list")
235244
self.assertFalse(
@@ -665,12 +674,12 @@ def test_trajectory_simple_listing(self):
665674

666675
for nid in self.cmd_to_nodeid_map[sub_cmd]:
667676
if str(nid) not in out_str:
668-
self.fail("The data objects ({}) with ids {} and {} "
669-
"were not found. "
670-
.format(sub_cmd,
671-
str(self.cmd_to_nodeid_map[sub_cmd][0]),
672-
str(self.cmd_to_nodeid_map[sub_cmd][1])) +
673-
"The output was {}".format(out_str))
677+
self.fail(
678+
"The data objects ({}) with ids {} and {} "
679+
"were not found. ".format(
680+
sub_cmd, str(self.cmd_to_nodeid_map[sub_cmd][0]),
681+
str(self.cmd_to_nodeid_map[sub_cmd][1])) +
682+
"The output was {}".format(out_str))
674683

675684
def test_trajectory_all_user_listing(self):
676685
from aiida.cmdline.commands.data import _Bands
@@ -742,10 +751,10 @@ def test_trajectory_group_listing(self):
742751
from aiida.cmdline.commands.data import _Cif
743752
from aiida.cmdline.commands.data import _Trajectory
744753

745-
args_to_test = [['-g', self.group_name], [
746-
'--group-name', self.group_name
747-
], ['-G', str(self.group_id)], ['--group-pk',
748-
str(self.group_id)]]
754+
args_to_test = [['-g', self.group_name],
755+
['--group-name', self.group_name],
756+
['-G', str(self.group_id)],
757+
['--group-pk', str(self.group_id)]]
749758

750759
sub_cmds = [_Bands, _Structure, _Cif, _Trajectory]
751760
for sub_cmd in sub_cmds:
@@ -755,8 +764,8 @@ def test_trajectory_group_listing(self):
755764
curr_scmd.list(*arg)
756765
out_str = ' '.join(output)
757766

758-
if str(self.cmd_to_nodeid_map_for_groups[
759-
sub_cmd]) not in out_str:
767+
if str(self.
768+
cmd_to_nodeid_map_for_groups[sub_cmd]) not in out_str:
760769
self.fail(
761770
"The data object ({}) with id {} "
762771
"was not found. ".format(

aiida/control/postgres.py

+52-19
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
installed by default on various systems. If the postgres setup is not the
1616
default installation, additional information needs to be provided.
1717
"""
18+
1819
try:
1920
import subprocess32 as subprocess
2021
except ImportError:
@@ -64,17 +65,24 @@ class Postgres(object):
6465
print('setup sucessful!')
6566
"""
6667

67-
def __init__(self, port=None, interactive=False, quiet=True):
68+
def __init__(self,
69+
host='localhost',
70+
port=None,
71+
interactive=False,
72+
quiet=True):
6873
self.interactive = interactive
6974
self.quiet = quiet
7075
self.pg_execute = _pg_execute_not_connected
7176
self.dbinfo = {}
72-
if port:
73-
self.set_port(port)
7477
self.setup_fail_callback = None
7578
self.setup_fail_counter = 0
7679
self.setup_max_tries = 1
7780

81+
if host:
82+
self.set_host(host)
83+
if port:
84+
self.set_port(port)
85+
7886
def set_setup_fail_callback(self, callback):
7987
"""
8088
Set a callback to be called when setup cannot be determined automatically
@@ -83,6 +91,10 @@ def set_setup_fail_callback(self, callback):
8391
"""
8492
self.setup_fail_callback = callback
8593

94+
def set_host(self, host):
95+
"""Set the host manually"""
96+
self.dbinfo['host'] = host
97+
8698
def set_port(self, port):
8799
"""Set the port manually"""
88100
self.dbinfo['port'] = str(port)
@@ -106,13 +118,19 @@ def determine_setup(self):
106118
self.dbinfo = local_dbinfo
107119
break
108120

109-
# This will work for the default Debian postgres setup
121+
# This will work for the default Debian postgres setup, assuming that sudo is available to the user
110122
if self.pg_execute == _pg_execute_not_connected:
111-
dbinfo['user'] = 'postgres'
112-
if _try_subcmd(
113-
non_interactive=bool(not self.interactive), **dbinfo):
114-
self.pg_execute = _pg_execute_sh
115-
self.dbinfo = dbinfo
123+
# Check if the user can find the sudo command
124+
if _sudo_exists():
125+
dbinfo['user'] = 'postgres'
126+
if _try_subcmd(
127+
non_interactive=bool(not self.interactive), **dbinfo):
128+
self.pg_execute = _pg_execute_sh
129+
self.dbinfo = dbinfo
130+
else:
131+
click.echo(
132+
'Warning: Could not find `sudo`. No way of connecting to the database could be found.'
133+
)
116134

117135
# This is to allow for any other setup
118136
if self.pg_execute == _pg_execute_not_connected:
@@ -192,9 +210,9 @@ def db_exists(self, dbname):
192210
def _no_setup_detected(self):
193211
"""Print a warning message and calls the failed setup callback"""
194212
message = '\n'.join([
195-
'Detected no known postgres setup, some information is needed to create the aiida database and grant aiida access to it.',
196-
'If you feel unsure about the following parameters, first check if postgresql is installed.',
197-
'If postgresql is not installed please exit and install it, then run verdi quicksetup again.',
213+
'Detected no known postgres setup, some information is needed to create the aiida database and grant ',
214+
'aiida access to it. If you feel unsure about the following parameters, first check if postgresql is ',
215+
'installed. If postgresql is not installed please exit and install it, then run verdi quicksetup again.',
198216
'If postgresql is installed, please ask your system manager to provide you with the following parameters:'
199217
])
200218
if not self.quiet:
@@ -269,6 +287,22 @@ def _try_connect(**kwargs):
269287
return success
270288

271289

290+
def _sudo_exists():
291+
"""
292+
Check that the sudo command can be found
293+
294+
:return: True if successful, False otherwise
295+
"""
296+
try:
297+
subprocess.check_output(['sudo', '-V'])
298+
except subprocess.CalledProcessError:
299+
return False
300+
except OSError:
301+
return False
302+
303+
return True
304+
305+
272306
def _try_subcmd(**kwargs):
273307
"""
274308
try to run psql in a subprocess.
@@ -286,12 +320,12 @@ def _try_subcmd(**kwargs):
286320

287321

288322
def _pg_execute_psyco(command, **kwargs):
289-
'''
323+
"""
290324
executes a postgres commandline through psycopg2
291325
292326
:param command: A psql command line as a str
293327
:param kwargs: will be forwarded to psycopg2.connect
294-
'''
328+
"""
295329
from psycopg2 import connect, ProgrammingError
296330
conn = connect(**kwargs)
297331
conn.autocommit = True
@@ -316,7 +350,7 @@ def _pg_execute_sh(command, user='postgres', **kwargs):
316350
:param kwargs: connection details to forward to psql, signature as in psycopg2.connect
317351
318352
To stop `sudo` from asking for a password and fail if one is required,
319-
pass `noninteractive=True` as a kwarg.
353+
pass `non_interactive=True` as a kwarg.
320354
"""
321355
options = ''
322356
database = kwargs.pop('database', None)
@@ -330,7 +364,7 @@ def _pg_execute_sh(command, user='postgres', **kwargs):
330364
if port:
331365
options += '-p {}'.format(port)
332366

333-
## build command line
367+
# Build command line
334368
sudo_cmd = ['sudo']
335369
non_interactive = kwargs.pop('non_interactive', None)
336370
if non_interactive:
@@ -343,10 +377,9 @@ def _pg_execute_sh(command, user='postgres', **kwargs):
343377
]
344378
sudo_su_psql = sudo_cmd + su_cmd + psql_cmd
345379
result = subprocess.check_output(sudo_su_psql, **kwargs)
380+
result = result.decode('utf-8').strip().split('\n')
381+
result = [i for i in result if i]
346382

347-
if isinstance(result, str):
348-
result = result.strip().split('\n')
349-
result = [i for i in result if i]
350383
return result
351384

352385

aiida/orm/data/cif.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,8 @@ class CifData(SinglefileData):
364364
when setting ``ase`` or ``values``, a physical CIF file is generated
365365
first, the values are updated from the physical CIF file.
366366
"""
367-
_set_incompatibilities = [('ase', 'file'), ('ase', 'values'), ('file',
368-
'values')]
367+
_set_incompatibilities = [('ase', 'file'), ('ase', 'values'),
368+
('file', 'values')]
369369
_scan_types = ['standard', 'flex']
370370
_parse_policies = ['eager', 'lazy']
371371

@@ -453,8 +453,8 @@ def get_or_create(cls, filename, use_first=False, store_cif=True):
453453
else:
454454
raise ValueError("More than one copy of a CIF file "
455455
"with the same MD5 has been found in "
456-
"the DB. pks={}".format(
457-
",".join([str(i.pk) for i in cifs])))
456+
"the DB. pks={}".format(",".join(
457+
[str(i.pk) for i in cifs])))
458458
else:
459459
return cifs[0], False
460460

aiida/restapi/resources.py

+3-9
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ def __init__(self, **kwargs):
2424
# Configure utils
2525
utils_conf_keys = ('PREFIX', 'PERPAGE_DEFAULT', 'LIMIT_DEFAULT')
2626
self.utils_confs = {
27-
k: kwargs[k]
28-
for k in utils_conf_keys
29-
if k in kwargs
27+
k: kwargs[k] for k in utils_conf_keys if k in kwargs
3028
}
3129
self.utils = Utils(**self.utils_confs)
3230

@@ -103,9 +101,7 @@ def __init__(self, **kwargs):
103101
# Configure utils
104102
utils_conf_keys = ('PREFIX', 'PERPAGE_DEFAULT', 'LIMIT_DEFAULT')
105103
self.utils_confs = {
106-
k: kwargs[k]
107-
for k in utils_conf_keys
108-
if k in kwargs
104+
k: kwargs[k] for k in utils_conf_keys if k in kwargs
109105
}
110106
self.utils = Utils(**self.utils_confs)
111107
self.method_decorators = {'get': kwargs.get('get_decorators', [])}
@@ -205,9 +201,7 @@ def __init__(self, **kwargs):
205201
# Configure utils
206202
utils_conf_keys = ('PREFIX', 'PERPAGE_DEFAULT', 'LIMIT_DEFAULT')
207203
self.utils_confs = {
208-
k: kwargs[k]
209-
for k in utils_conf_keys
210-
if k in kwargs
204+
k: kwargs[k] for k in utils_conf_keys if k in kwargs
211205
}
212206
self.utils = Utils(**self.utils_confs)
213207
self.method_decorators = {'get': kwargs.get('get_decorators', [])}

docs/update_req_for_rtd.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ def update_req_for_rtd(pre_commit):
2626

2727
import setup_requirements
2828

29-
reqs = set(
30-
setup_requirements.extras_require['docs'] + setup_requirements.
31-
extras_require['rest'] + setup_requirements.extras_require['testing'] +
32-
setup_requirements.extras_require[':python_version < "3"'] +
33-
# To avoid that it requires also the postgres libraries
34-
[
35-
p for p in setup_requirements.install_requires
36-
if not p.startswith('psycopg2')
37-
])
29+
reqs = set(setup_requirements.extras_require['docs'] +
30+
setup_requirements.extras_require['rest'] +
31+
setup_requirements.extras_require['testing'] +
32+
setup_requirements.extras_require[':python_version < "3"'] +
33+
# To avoid that it requires also the postgres libraries
34+
[
35+
p for p in setup_requirements.install_requires
36+
if not p.startswith('psycopg2')
37+
])
3838
reqs_str = "\n".join(sorted(reqs))
3939

4040
basename = 'requirements_for_rtd.txt'

0 commit comments

Comments
 (0)