ert 19.0.1__py3-none-any.whl → 20.0.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. ert/__main__.py +94 -63
  2. ert/analysis/_es_update.py +11 -14
  3. ert/cli/main.py +1 -1
  4. ert/config/__init__.py +3 -2
  5. ert/config/_create_observation_dataframes.py +52 -375
  6. ert/config/_observations.py +527 -200
  7. ert/config/_read_summary.py +4 -5
  8. ert/config/ert_config.py +52 -117
  9. ert/config/everest_control.py +40 -39
  10. ert/config/everest_response.py +3 -15
  11. ert/config/field.py +4 -76
  12. ert/config/forward_model_step.py +17 -1
  13. ert/config/gen_data_config.py +14 -17
  14. ert/config/observation_config_migrations.py +821 -0
  15. ert/config/parameter_config.py +18 -28
  16. ert/config/parsing/__init__.py +0 -1
  17. ert/config/parsing/_parse_zonemap.py +45 -0
  18. ert/config/parsing/config_keywords.py +1 -0
  19. ert/config/parsing/config_schema.py +2 -0
  20. ert/config/parsing/observations_parser.py +2 -0
  21. ert/config/response_config.py +5 -23
  22. ert/config/rft_config.py +129 -31
  23. ert/config/summary_config.py +1 -13
  24. ert/config/surface_config.py +0 -57
  25. ert/dark_storage/compute/misfits.py +0 -42
  26. ert/dark_storage/endpoints/__init__.py +0 -2
  27. ert/dark_storage/endpoints/experiments.py +2 -5
  28. ert/dark_storage/json_schema/experiment.py +1 -2
  29. ert/field_utils/__init__.py +0 -2
  30. ert/field_utils/field_utils.py +1 -117
  31. ert/gui/ertwidgets/listeditbox.py +9 -1
  32. ert/gui/ertwidgets/models/ertsummary.py +20 -6
  33. ert/gui/ertwidgets/pathchooser.py +9 -1
  34. ert/gui/ertwidgets/stringbox.py +11 -3
  35. ert/gui/ertwidgets/textbox.py +10 -3
  36. ert/gui/ertwidgets/validationsupport.py +19 -1
  37. ert/gui/main_window.py +11 -6
  38. ert/gui/simulation/experiment_panel.py +1 -1
  39. ert/gui/simulation/run_dialog.py +11 -1
  40. ert/gui/tools/manage_experiments/export_dialog.py +4 -0
  41. ert/gui/tools/manage_experiments/manage_experiments_panel.py +1 -0
  42. ert/gui/tools/manage_experiments/storage_info_widget.py +1 -1
  43. ert/gui/tools/manage_experiments/storage_widget.py +21 -4
  44. ert/gui/tools/plot/data_type_proxy_model.py +1 -1
  45. ert/gui/tools/plot/plot_api.py +35 -27
  46. ert/gui/tools/plot/plot_widget.py +5 -0
  47. ert/gui/tools/plot/plot_window.py +4 -7
  48. ert/run_models/ensemble_experiment.py +2 -9
  49. ert/run_models/ensemble_smoother.py +1 -9
  50. ert/run_models/everest_run_model.py +31 -23
  51. ert/run_models/initial_ensemble_run_model.py +19 -22
  52. ert/run_models/manual_update.py +11 -5
  53. ert/run_models/model_factory.py +7 -7
  54. ert/run_models/multiple_data_assimilation.py +3 -16
  55. ert/sample_prior.py +12 -14
  56. ert/scheduler/job.py +24 -4
  57. ert/services/__init__.py +7 -3
  58. ert/services/_storage_main.py +59 -22
  59. ert/services/ert_server.py +186 -24
  60. ert/shared/version.py +3 -3
  61. ert/storage/local_ensemble.py +50 -116
  62. ert/storage/local_experiment.py +94 -109
  63. ert/storage/local_storage.py +10 -12
  64. ert/storage/migration/to24.py +26 -0
  65. ert/storage/migration/to25.py +91 -0
  66. ert/utils/__init__.py +20 -0
  67. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/METADATA +4 -51
  68. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/RECORD +80 -83
  69. everest/bin/everest_script.py +5 -5
  70. everest/bin/kill_script.py +2 -2
  71. everest/bin/monitor_script.py +2 -2
  72. everest/bin/utils.py +4 -4
  73. everest/detached/everserver.py +6 -6
  74. everest/gui/everest_client.py +0 -6
  75. everest/gui/main_window.py +2 -2
  76. everest/util/__init__.py +1 -19
  77. ert/dark_storage/compute/__init__.py +0 -0
  78. ert/dark_storage/endpoints/compute/__init__.py +0 -0
  79. ert/dark_storage/endpoints/compute/misfits.py +0 -95
  80. ert/services/_base_service.py +0 -387
  81. ert/services/webviz_ert_service.py +0 -20
  82. ert/shared/storage/command.py +0 -38
  83. ert/shared/storage/extraction.py +0 -42
  84. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/WHEEL +0 -0
  85. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/entry_points.txt +0 -0
  86. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/licenses/COPYING +0 -0
  87. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/top_level.txt +0 -0
ert/scheduler/job.py CHANGED
@@ -482,6 +482,7 @@ async def log_warnings_from_forward_model(
482
482
  real: Realization,
483
483
  job_submission_time: float,
484
484
  timeout_seconds: int = Job.DEFAULT_FILE_VERIFICATION_TIMEOUT,
485
+ max_logged_warnings: int = 200,
485
486
  ) -> int:
486
487
  """Parse all stdout and stderr files from running the forward model
487
488
  for anything that looks like a Warning, and log it.
@@ -513,11 +514,19 @@ async def log_warnings_from_forward_model(
513
514
  )
514
515
 
515
516
  async def log_warnings_from_file(
516
- file: Path, iens: int, step: ForwardModelStep, step_idx: int, filetype: str
517
- ) -> None:
517
+ file: Path,
518
+ iens: int,
519
+ step: ForwardModelStep,
520
+ step_idx: int,
521
+ filetype: str,
522
+ max_warnings_to_log: int,
523
+ ) -> int:
524
+ """Returns how many times a warning was logged"""
518
525
  captured: list[str] = []
519
526
  file_text = await anyio.Path(file).read_text(encoding="utf-8")
520
527
  for line in file_text.splitlines():
528
+ if len(captured) >= max_warnings_to_log:
529
+ break
521
530
  if line_contains_warning(line):
522
531
  captured.append(line[:max_length])
523
532
 
@@ -528,6 +537,7 @@ async def log_warnings_from_forward_model(
528
537
  )
529
538
  warnings.warn(warning_msg, PostSimulationWarning, stacklevel=2)
530
539
  logger.warning(warning_msg)
540
+ return len(captured)
531
541
 
532
542
  async def wait_for_file(file_path: Path, _timeout: int) -> int:
533
543
  if _timeout <= 0:
@@ -546,6 +556,7 @@ async def log_warnings_from_forward_model(
546
556
  break
547
557
  return remaining_timeout
548
558
 
559
+ log_count = 0
549
560
  with suppress(KeyError):
550
561
  runpath = Path(real.run_arg.runpath)
551
562
  for step_idx, step in enumerate(real.fm_steps):
@@ -560,9 +571,18 @@ async def log_warnings_from_forward_model(
560
571
  if timeout_seconds <= 0:
561
572
  break
562
573
 
563
- await log_warnings_from_file(
564
- std_path, real.iens, step, step_idx, file_type
574
+ log_count += await log_warnings_from_file(
575
+ std_path,
576
+ real.iens,
577
+ step,
578
+ step_idx,
579
+ file_type,
580
+ max_logged_warnings - log_count,
565
581
  )
566
582
  if timeout_seconds <= 0:
567
583
  break
584
+ if log_count >= max_logged_warnings:
585
+ logger.warning(
586
+ "Reached maximum number of forward model step warnings to extract"
587
+ )
568
588
  return timeout_seconds
ert/services/__init__.py CHANGED
@@ -1,4 +1,8 @@
1
- from .ert_server import ErtServer
2
- from .webviz_ert_service import WebvizErt
1
+ from .ert_server import (
2
+ ErtServer,
3
+ ErtServerExit,
4
+ ServerBootFail,
5
+ create_ertserver_client,
6
+ )
3
7
 
4
- __all__ = ["ErtServer", "WebvizErt"]
8
+ __all__ = ["ErtServer", "ErtServerExit", "ServerBootFail", "create_ertserver_client"]
@@ -13,6 +13,7 @@ import sys
13
13
  import threading
14
14
  import time
15
15
  import warnings
16
+ from argparse import ArgumentParser
16
17
  from base64 import b64encode
17
18
  from pathlib import Path
18
19
  from typing import Any
@@ -29,12 +30,11 @@ from uvicorn.supervisors import ChangeReload
29
30
 
30
31
  from ert.logging import STORAGE_LOG_CONFIG
31
32
  from ert.plugins import setup_site_logging
32
- from ert.services._base_service import BaseServiceExit
33
+ from ert.services import ErtServerExit
33
34
  from ert.shared import __file__ as ert_shared_path
34
35
  from ert.shared import find_available_socket, get_machine_name
35
- from ert.shared.storage.command import add_parser_options
36
36
  from ert.trace import tracer
37
- from everest.util import makedirs_if_needed
37
+ from ert.utils import makedirs_if_needed
38
38
 
39
39
  DARK_STORAGE_APP = "ert.dark_storage.app:app"
40
40
 
@@ -82,7 +82,7 @@ def _get_host_list() -> list[str]:
82
82
 
83
83
 
84
84
  def _create_connection_info(
85
- sock: socket.socket, authtoken: str, cert: str | os.PathLike[str]
85
+ sock: socket.socket, authtoken: str, cert: str | os.PathLike[str] | Path
86
86
  ) -> dict[str, Any]:
87
87
  connection_info = {
88
88
  "urls": [
@@ -91,7 +91,7 @@ def _create_connection_info(
91
91
  "authtoken": authtoken,
92
92
  "host": get_machine_name(),
93
93
  "port": sock.getsockname()[1],
94
- "cert": cert,
94
+ "cert": str(cert),
95
95
  "auth": authtoken,
96
96
  }
97
97
 
@@ -102,14 +102,17 @@ def _create_connection_info(
102
102
  return connection_info
103
103
 
104
104
 
105
- def _generate_certificate(cert_folder: str) -> tuple[str, str, bytes]:
105
+ def _generate_certificate(cert_folder: Path) -> tuple[Path, Path, bytes]:
106
106
  """Generate a private key and a certificate signed with it
107
107
 
108
108
  Both the certificate and the key are written to files in the folder given
109
109
  by `get_certificate_dir(config)`. The key is encrypted before being
110
110
  stored.
111
- Returns the path to the certificate file, the path to the key file, and
112
- the password used for encrypting the key
111
+
112
+ Returns a 3-tuple with
113
+ * Certificate file path
114
+ * Key file path
115
+ * Password used for encrypting the key
113
116
  """
114
117
  # Generate private key
115
118
  key = rsa.generate_private_key(
@@ -150,11 +153,11 @@ def _generate_certificate(cert_folder: str) -> tuple[str, str, bytes]:
150
153
 
151
154
  # Write certificate and key to disk
152
155
  makedirs_if_needed(cert_folder)
153
- cert_path = os.path.join(cert_folder, dns_name + ".crt")
154
- Path(cert_path).write_bytes(cert.public_bytes(serialization.Encoding.PEM))
155
- key_path = os.path.join(cert_folder, dns_name + ".key")
156
+ cert_path = cert_folder / f"{dns_name}.crt"
157
+ cert_path.write_bytes(cert.public_bytes(serialization.Encoding.PEM))
158
+ key_path = cert_folder / f"{dns_name}.key"
156
159
  pw = bytes(os.urandom(28))
157
- Path(key_path).write_bytes(
160
+ key_path.write_bytes(
158
161
  key.private_bytes(
159
162
  encoding=serialization.Encoding.PEM,
160
163
  format=serialization.PrivateFormat.TraditionalOpenSSL,
@@ -184,16 +187,16 @@ def run_server(
184
187
 
185
188
  config_args: dict[str, Any] = {}
186
189
  if args.debug or debug:
187
- config_args.update(reload=True, reload_dirs=[os.path.dirname(ert_shared_path)])
190
+ config_args.update(reload=True, reload_dirs=[Path(ert_shared_path).parent])
188
191
  os.environ["ERT_STORAGE_DEBUG"] = "1"
189
192
 
190
- sock = find_available_socket(
193
+ sock: socket.socket = find_available_socket(
191
194
  host=get_machine_name(), port_range=range(51850, 51870 + 1)
192
195
  )
193
196
 
194
197
  # Appropriated from uvicorn.main:run
195
198
  os.environ["ERT_STORAGE_NO_TOKEN"] = "1"
196
- os.environ["ERT_STORAGE_ENS_PATH"] = os.path.abspath(args.project)
199
+ os.environ["ERT_STORAGE_ENS_PATH"] = str(args.project.absolute())
197
200
  config = (
198
201
  # uvicorn.Config() resets the logging config (overriding additional
199
202
  # handlers added to loggers like e.g. the ert_azurelogger handler
@@ -253,11 +256,11 @@ def _join_terminate_thread(terminate_on_parent_death_thread: threading.Thread) -
253
256
  """Join the terminate thread, handling BaseServiceExit (which is used by Everest)"""
254
257
  try:
255
258
  terminate_on_parent_death_thread.join()
256
- except BaseServiceExit:
259
+ except ErtServerExit:
257
260
  logger = logging.getLogger("ert.shared.storage.info")
258
261
  logger.info(
259
262
  "Got BaseServiceExit while joining terminate thread, "
260
- "as expected from _base_service.py"
263
+ "as expected from ert_server.py"
261
264
  )
262
265
 
263
266
 
@@ -265,9 +268,7 @@ def main() -> None:
265
268
  args = parse_args()
266
269
  authentication = _generate_authentication()
267
270
  os.environ["ERT_STORAGE_TOKEN"] = authentication
268
- cert_path, key_path, key_pw = _generate_certificate(
269
- os.path.join(args.project, "cert")
270
- )
271
+ cert_path, key_path, key_pw = _generate_certificate(args.project / "cert")
271
272
  config_args: dict[str, Any] = {
272
273
  "ssl_keyfile": key_path,
273
274
  "ssl_certfile": cert_path,
@@ -283,7 +284,7 @@ def main() -> None:
283
284
  warnings.filterwarnings("ignore", category=DeprecationWarning)
284
285
 
285
286
  if args.debug:
286
- config_args.update(reload=True, reload_dirs=[os.path.dirname(ert_shared_path)])
287
+ config_args.update(reload=True, reload_dirs=[Path(ert_shared_path).parent])
287
288
 
288
289
  # Need to run uvicorn.Config before entering the ErtPluginContext because
289
290
  # uvicorn.Config overrides the configuration of existing loggers, thus removing
@@ -310,12 +311,48 @@ def main() -> None:
310
311
  logger.info("Starting dark storage")
311
312
  logger.info(f"Started dark storage with parent {args.parent_pid}")
312
313
  run_server(args, debug=False, uvicorn_config=uvicorn_config)
313
- except (SystemExit, BaseServiceExit):
314
+ except (SystemExit, ErtServerExit):
314
315
  logger.info("Stopping dark storage")
315
316
  finally:
316
317
  stopped.set()
317
318
  _join_terminate_thread(terminate_on_parent_death_thread)
318
319
 
319
320
 
321
+ def add_parser_options(ap: ArgumentParser) -> None:
322
+ ap.add_argument(
323
+ "config",
324
+ type=str,
325
+ help=("ERT config file to start the server from "),
326
+ nargs="?", # optional
327
+ )
328
+ ap.add_argument(
329
+ "--project",
330
+ "-p",
331
+ type=Path,
332
+ help="Path to directory in which to create storage_server.json",
333
+ default=Path.cwd(),
334
+ )
335
+ ap.add_argument(
336
+ "--traceparent",
337
+ type=str,
338
+ help="Trace parent id to be used by the storage root span",
339
+ default=None,
340
+ )
341
+ ap.add_argument(
342
+ "--parent_pid",
343
+ type=int,
344
+ help="The parent process id",
345
+ default=os.getppid(),
346
+ )
347
+ ap.add_argument(
348
+ "--host", type=str, default=os.environ.get("ERT_STORAGE_HOST", "127.0.0.1")
349
+ )
350
+ ap.add_argument("--logging-config", type=str, default=None)
351
+ ap.add_argument(
352
+ "--verbose", action="store_true", help="Show verbose output.", default=False
353
+ )
354
+ ap.add_argument("--debug", action="store_true", default=False)
355
+
356
+
320
357
  if __name__ == "__main__":
321
358
  main()
@@ -1,23 +1,187 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
4
+ import io
3
5
  import json
4
6
  import logging
5
7
  import os
8
+ import signal
6
9
  import sys
7
10
  import threading
8
11
  import types
9
- from collections.abc import Mapping
12
+ from collections.abc import Callable, Mapping, Sequence
10
13
  from pathlib import Path
14
+ from select import PIPE_BUF, select
15
+ from subprocess import Popen, TimeoutExpired
11
16
  from tempfile import NamedTemporaryFile
12
17
  from time import sleep
13
- from typing import Any, cast
18
+ from typing import Any, TypedDict, cast
14
19
 
15
20
  import requests
16
21
 
17
22
  from ert.dark_storage.client import Client, ErtClientConnectionInfo
18
- from ert.services._base_service import ErtServerConnectionInfo, _Proc
19
23
  from ert.trace import get_traceparent
20
24
 
25
+ SERVICE_CONF_PATHS: set[str] = set()
26
+
27
+
28
+ class ErtServerConnectionInfo(TypedDict):
29
+ urls: list[str]
30
+ authtoken: str
31
+ host: str
32
+ port: str
33
+ cert: str
34
+ auth: str
35
+
36
+
37
+ class ErtServerExit(OSError):
38
+ pass
39
+
40
+
41
+ def cleanup_service_files(signum: int, frame: types.FrameType | None) -> None:
42
+ for file_path in SERVICE_CONF_PATHS:
43
+ file = Path(file_path)
44
+ if file.exists():
45
+ file.unlink()
46
+ raise ErtServerExit(f"Signal {signum} received.")
47
+
48
+
49
+ if threading.current_thread() is threading.main_thread():
50
+ signal.signal(signal.SIGTERM, cleanup_service_files)
51
+ signal.signal(signal.SIGINT, cleanup_service_files)
52
+
53
+
54
+ class ServerBootFail(RuntimeError):
55
+ pass
56
+
57
+
58
+ class _Proc(threading.Thread):
59
+ def __init__(
60
+ self,
61
+ service_name: str,
62
+ exec_args: Sequence[str],
63
+ timeout: int,
64
+ on_connection_info_received: Callable[
65
+ [ErtServerConnectionInfo | Exception | None], None
66
+ ],
67
+ project: Path,
68
+ ) -> None:
69
+ super().__init__()
70
+
71
+ self._shutdown = threading.Event()
72
+
73
+ self._service_name = service_name
74
+ self._exec_args = exec_args
75
+ self._timeout = timeout
76
+ self._propagate_connection_info_from_childproc = on_connection_info_received
77
+ self._service_config_path = project / f"{self._service_name}_server.json"
78
+
79
+ fd_read, fd_write = os.pipe()
80
+ self._comm_pipe = os.fdopen(fd_read)
81
+
82
+ env = os.environ.copy()
83
+ env["ERT_COMM_FD"] = str(fd_write)
84
+
85
+ SERVICE_CONF_PATHS.add(str(self._service_config_path))
86
+
87
+ # The process is waited for in _do_shutdown()
88
+ self._childproc = Popen(
89
+ self._exec_args,
90
+ pass_fds=(fd_write,),
91
+ env=env,
92
+ close_fds=True,
93
+ )
94
+ os.close(fd_write)
95
+
96
+ def run(self) -> None:
97
+ comm = self._read_connection_info_from_process(self._childproc)
98
+
99
+ if comm is None:
100
+ self._propagate_connection_info_from_childproc(TimeoutError())
101
+ return # _read_conn_info() has already cleaned up in this case
102
+
103
+ conn_info: ErtServerConnectionInfo | Exception | None = None
104
+ try:
105
+ conn_info = json.loads(comm)
106
+ except json.JSONDecodeError:
107
+ conn_info = ServerBootFail()
108
+ except Exception as exc:
109
+ conn_info = exc
110
+
111
+ try:
112
+ self._propagate_connection_info_from_childproc(conn_info)
113
+
114
+ while True:
115
+ if self._childproc.poll() is not None:
116
+ break
117
+ if self._shutdown.wait(1):
118
+ self._do_shutdown()
119
+ break
120
+
121
+ except Exception as e:
122
+ print(str(e))
123
+ self.logger.exception(e)
124
+
125
+ finally:
126
+ self._ensure_connection_info_file_is_deleted()
127
+
128
+ def shutdown(self) -> int:
129
+ """Shutdown the server."""
130
+ self._shutdown.set()
131
+ self.join()
132
+
133
+ return self._childproc.returncode
134
+
135
+ def _read_connection_info_from_process(self, proc: Popen[bytes]) -> str | None:
136
+ comm_buf = io.StringIO()
137
+ first_iter = True
138
+ while first_iter or proc.poll() is None:
139
+ first_iter = False
140
+ ready = select([self._comm_pipe], [], [], self._timeout)
141
+
142
+ # Timeout reached, exit with a failure
143
+ if ready == ([], [], []):
144
+ self._do_shutdown()
145
+ self._ensure_connection_info_file_is_deleted()
146
+ return None
147
+
148
+ x = self._comm_pipe.read(PIPE_BUF)
149
+ if not x: # EOF
150
+ break
151
+ comm_buf.write(x)
152
+ return comm_buf.getvalue()
153
+
154
+ def _do_shutdown(self) -> None:
155
+ if self._childproc is None:
156
+ return
157
+ try:
158
+ self._childproc.terminate()
159
+ self._childproc.wait(10) # Give it 10s to shut down cleanly..
160
+ except TimeoutExpired:
161
+ try:
162
+ self._childproc.kill() # ... then kick it harder...
163
+ self._childproc.wait(self._timeout) # ... and wait again
164
+ except TimeoutExpired:
165
+ self.logger.error(
166
+ f"waiting for child-process exceeded timeout {self._timeout}s"
167
+ )
168
+
169
+ def _ensure_connection_info_file_is_deleted(self) -> None:
170
+ """
171
+ Ensure that the JSON connection information file is deleted
172
+ """
173
+ with contextlib.suppress(OSError):
174
+ if self._service_config_path.exists():
175
+ self._service_config_path.unlink()
176
+
177
+ @property
178
+ def logger(self) -> logging.Logger:
179
+ return logging.getLogger("ert.shared.storage")
180
+
181
+
182
+ _ERT_SERVER_CONNECTION_INFO_FILE = "storage_server.json"
183
+ _ERT_SERVER_EXECUTABLE_FILE = str(Path(__file__).parent / "_storage_main.py")
184
+
21
185
 
22
186
  class ErtServerContext:
23
187
  def __init__(self, service: ErtServer) -> None:
@@ -70,7 +234,7 @@ class ErtServer:
70
234
 
71
235
  run_storage_main_cmd = [
72
236
  sys.executable,
73
- str(Path(__file__).parent / "_storage_main.py"),
237
+ _ERT_SERVER_EXECUTABLE_FILE,
74
238
  "--project",
75
239
  storage_path,
76
240
  ]
@@ -91,7 +255,7 @@ class ErtServer:
91
255
  self._thread_that_starts_server_process = _Proc(
92
256
  service_name="storage",
93
257
  exec_args=run_storage_main_cmd,
94
- timeout=120,
258
+ timeout=timeout,
95
259
  on_connection_info_received=self.on_connection_info_received_from_server_process,
96
260
  project=Path(self._storage_path),
97
261
  )
@@ -167,21 +331,6 @@ class ErtServer:
167
331
  "None of the URLs provided for the ert storage server worked."
168
332
  )
169
333
 
170
- @classmethod
171
- def session(cls, project: os.PathLike[str], timeout: int | None = None) -> Client:
172
- """
173
- Start a HTTP transaction with the server
174
- """
175
- inst = cls.connect(timeout=timeout, project=project)
176
- info = inst.fetch_connection_info()
177
- return Client(
178
- conn_info=ErtClientConnectionInfo(
179
- base_url=inst.fetch_url(),
180
- auth_token=inst.fetch_auth()[1],
181
- cert=info["cert"],
182
- )
183
- )
184
-
185
334
  @property
186
335
  def logger(self) -> logging.Logger:
187
336
  return logging.getLogger("ert.shared.storage")
@@ -218,12 +367,12 @@ class ErtServer:
218
367
  timeout = 240
219
368
  t = -1
220
369
  while t < timeout:
221
- storage_server_path = path / "storage_server.json"
370
+ storage_server_path = path / _ERT_SERVER_CONNECTION_INFO_FILE
222
371
  if (
223
372
  storage_server_path.exists()
224
373
  and storage_server_path.stat().st_size > 0
225
374
  ):
226
- with (path / "storage_server.json").open() as f:
375
+ with (path / _ERT_SERVER_CONNECTION_INFO_FILE).open() as f:
227
376
  storage_server_content = json.load(f)
228
377
 
229
378
  return ErtServer(
@@ -277,9 +426,9 @@ class ErtServer:
277
426
  if self._storage_path is not None:
278
427
  if not Path(self._storage_path).exists():
279
428
  raise RuntimeError(f"No storage exists at : {self._storage_path}")
280
- path = f"{self._storage_path}/storage_server.json"
429
+ path = f"{self._storage_path}/{_ERT_SERVER_CONNECTION_INFO_FILE}"
281
430
  else:
282
- path = "storage_server.json"
431
+ path = _ERT_SERVER_CONNECTION_INFO_FILE
283
432
 
284
433
  if isinstance(info, Mapping):
285
434
  with NamedTemporaryFile(dir=f"{self._storage_path}", delete=False) as f:
@@ -315,3 +464,16 @@ class ErtServer:
315
464
  def wait(self) -> None:
316
465
  if self._thread_that_starts_server_process is not None:
317
466
  self._thread_that_starts_server_process.join()
467
+
468
+
469
+ def create_ertserver_client(project: Path, timeout: int | None = None) -> Client:
470
+ """Read connection info from file in path and create HTTP client."""
471
+ connection = ErtServer.connect(timeout=timeout, project=project)
472
+ info = connection.fetch_connection_info()
473
+ return Client(
474
+ conn_info=ErtClientConnectionInfo(
475
+ base_url=connection.fetch_url(),
476
+ auth_token=connection.fetch_auth()[1],
477
+ cert=info["cert"],
478
+ )
479
+ )
ert/shared/version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '19.0.1'
32
- __version_tuple__ = version_tuple = (19, 0, 1)
31
+ __version__ = version = '20.0.0b1'
32
+ __version_tuple__ = version_tuple = (20, 0, 0, 'b1')
33
33
 
34
- __commit_id__ = commit_id = 'gd8015f3be'
34
+ __commit_id__ = commit_id = 'g4156859a8'