|
3 | 3 | """ |
4 | 4 |
|
5 | 5 | import logging |
6 | | -from datashield.interface import DSLoginInfo, DSConnection, DSDriver, DSError |
| 6 | +import os |
| 7 | +from datashield.interface import DSConfig, DSLoginInfo, DSConnection, DSDriver, DSError |
7 | 8 | import time |
8 | 9 |
|
| 10 | +# Default configuration file paths to look for DataSHIELD login information, in order of precedence |
| 11 | +CONFIG_FILES = ["./.datashield/config.yaml", "~/.datashield/config.yaml"] |
| 12 | + |
9 | 13 |
|
10 | 14 | class DSLoginBuilder: |
11 | 15 | """ |
12 | 16 | Helper class to formalize DataSHIELD login arguments for a set of servers. |
13 | 17 | """ |
14 | 18 |
|
15 | | - def __init__(self): |
| 19 | + def __init__(self, names: list[str] = None): |
| 20 | + """Create a builder, optionally loading login information from configuration files |
| 21 | + for the specified server names. |
| 22 | + |
| 23 | + :param names: The list of server names to load from configuration files, if any. If not defined, |
| 24 | + no login information will be loaded from configuration files. |
| 25 | + """ |
16 | 26 | self.items: list[DSLoginInfo] = [] |
| 27 | + # load login information from configuration files, in order of precedence |
| 28 | + if names is not None and len(names) > 0: |
| 29 | + # load configuration only once, do not merge results from multiple config files |
| 30 | + loaded = False |
| 31 | + for config_file in CONFIG_FILES: |
| 32 | + if loaded: |
| 33 | + break |
| 34 | + try: |
| 35 | + # check file exists and is readable, if not, silently ignore |
| 36 | + if not os.path.exists(config_file): |
| 37 | + continue |
| 38 | + if not os.access(config_file, os.R_OK): |
| 39 | + continue |
| 40 | + config = DSConfig.load_from_file(config_file) |
| 41 | + loaded = True |
| 42 | + if config.servers: |
| 43 | + items = [x for x in config.servers if x.name in names] |
| 44 | + if len(items) == 0: |
| 45 | + logging.warning(f"No matching server names found in {config_file} for: {', '.join(names)}") |
| 46 | + else: |
| 47 | + self.items.extend(items) |
| 48 | + except Exception as e: |
| 49 | + # silently ignore errors, e.g. file not found or invalid format |
| 50 | + logging.error(f"Failed to load login information from {config_file}: {e}") |
17 | 51 |
|
18 | 52 | def add( |
19 | 53 | self, |
@@ -46,7 +80,9 @@ def add( |
46 | 80 | raise ValueError(f"Server name must be unique: {name}") |
47 | 81 | if user is None and token is None: |
48 | 82 | raise ValueError("Either user or token must be provided") |
49 | | - self.items.append(DSLoginInfo(name, url, user, password, token, profile, driver)) |
| 83 | + self.items.append( |
| 84 | + DSLoginInfo(name=name, url=url, user=user, password=password, token=token, profile=profile, driver=driver) |
| 85 | + ) |
50 | 86 | return self |
51 | 87 |
|
52 | 88 | def remove(self, name: str): |
@@ -109,7 +145,7 @@ def open(self, restore: str = None, failSafe: bool = False) -> None: |
109 | 145 | raise e |
110 | 146 | if self.has_errors(): |
111 | 147 | for name in self.errors: |
112 | | - print(f"Connection to {name} has failed") |
| 148 | + logging.error(f"Connection to {name} has failed") |
113 | 149 |
|
114 | 150 | def close(self, save: str = None) -> None: |
115 | 151 | """ |
|
0 commit comments