fmu-settings 0.5.4__tar.gz → 0.6.1__tar.gz
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.
Potentially problematic release.
This version of fmu-settings might be problematic. Click here for more details.
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/PKG-INFO +1 -1
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/_fmu_dir.py +119 -19
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/_init.py +19 -32
- fmu_settings-0.6.1/src/fmu/settings/_readme_texts.py +34 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/_resources/cache_manager.py +13 -9
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/_version.py +3 -3
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/models/project_config.py +2 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/models/user_config.py +3 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu_settings.egg-info/PKG-INFO +1 -1
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu_settings.egg-info/SOURCES.txt +1 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/tests/conftest.py +3 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/tests/test_fmu_dir.py +83 -2
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/tests/test_init.py +3 -4
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/tests/test_resources/test_cache_manager.py +9 -17
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/tests/test_resources/test_lock_manager.py +18 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/tests/test_resources/test_resource_managers.py +6 -36
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/.coveragerc +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/.github/pull_request_template.md +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/.github/workflows/ci.yml +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/.github/workflows/codeql.yml +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/.github/workflows/publish.yml +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/.gitignore +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/CONTRIBUTING.md +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/LICENSE +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/README.md +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/SECURITY.md +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/pyproject.toml +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/setup.cfg +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/__init__.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/__init__.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/_global_config.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/_logging.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/_resources/__init__.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/_resources/config_managers.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/_resources/lock_manager.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/_resources/pydantic_resource_manager.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/models/__init__.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/models/_enums.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/models/_mappings.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/models/lock_info.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/py.typed +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/types.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu_settings.egg-info/dependency_links.txt +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu_settings.egg-info/requires.txt +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu_settings.egg-info/top_level.txt +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/tests/test_global_config.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/tests/test_resources/test_project_config.py +0 -0
- {fmu_settings-0.5.4 → fmu_settings-0.6.1}/tests/test_resources/test_user_config.py +0 -0
|
@@ -4,12 +4,13 @@ from pathlib import Path
|
|
|
4
4
|
from typing import TYPE_CHECKING, Any, Final, Self, TypeAlias, cast
|
|
5
5
|
|
|
6
6
|
from ._logging import null_logger
|
|
7
|
+
from ._readme_texts import PROJECT_README_CONTENT, USER_README_CONTENT
|
|
7
8
|
from ._resources.cache_manager import CacheManager
|
|
8
9
|
from ._resources.config_managers import (
|
|
9
10
|
ProjectConfigManager,
|
|
10
11
|
UserConfigManager,
|
|
11
12
|
)
|
|
12
|
-
from ._resources.lock_manager import LockManager
|
|
13
|
+
from ._resources.lock_manager import DEFAULT_LOCK_TIMEOUT, LockManager
|
|
13
14
|
from .models.project_config import ProjectConfig
|
|
14
15
|
from .models.user_config import UserConfig
|
|
15
16
|
|
|
@@ -24,13 +25,22 @@ class FMUDirectoryBase:
|
|
|
24
25
|
config: FMUConfigManager
|
|
25
26
|
_lock: LockManager
|
|
26
27
|
_cache_manager: CacheManager
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
_README_CONTENT: str = ""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self: Self,
|
|
32
|
+
base_path: str | Path,
|
|
33
|
+
cache_revisions: int = CacheManager.MIN_REVISIONS,
|
|
34
|
+
*,
|
|
35
|
+
lock_timeout_seconds: int = DEFAULT_LOCK_TIMEOUT,
|
|
36
|
+
) -> None:
|
|
29
37
|
"""Initializes access to a .fmu directory.
|
|
30
38
|
|
|
31
39
|
Args:
|
|
32
40
|
base_path: The directory containing the .fmu directory or one of its parent
|
|
33
41
|
dirs
|
|
42
|
+
cache_revisions: Number of revisions to retain in the cache. Minimum is 5.
|
|
43
|
+
lock_timeout_seconds: Lock expiration time in seconds. Default 20 minutes.
|
|
34
44
|
|
|
35
45
|
Raises:
|
|
36
46
|
FileExistsError: If .fmu exists but is not a directory
|
|
@@ -39,8 +49,8 @@ class FMUDirectoryBase:
|
|
|
39
49
|
"""
|
|
40
50
|
self.base_path = Path(base_path).resolve()
|
|
41
51
|
logger.debug(f"Initializing FMUDirectory from '{base_path}'")
|
|
42
|
-
self._lock = LockManager(self)
|
|
43
|
-
self._cache_manager = CacheManager(self, max_revisions=
|
|
52
|
+
self._lock = LockManager(self, timeout_seconds=lock_timeout_seconds)
|
|
53
|
+
self._cache_manager = CacheManager(self, max_revisions=cache_revisions)
|
|
44
54
|
|
|
45
55
|
fmu_dir = self.base_path / ".fmu"
|
|
46
56
|
if fmu_dir.exists():
|
|
@@ -72,8 +82,15 @@ class FMUDirectoryBase:
|
|
|
72
82
|
|
|
73
83
|
@cache_max_revisions.setter
|
|
74
84
|
def cache_max_revisions(self: Self, value: int) -> None:
|
|
75
|
-
"""Update the retention limit for revision snapshots.
|
|
76
|
-
|
|
85
|
+
"""Update the retention limit for revision snapshots.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
value: The new maximum number of revisions to retain. Minimum value is 5.
|
|
89
|
+
Values below 5 are set to 5.
|
|
90
|
+
"""
|
|
91
|
+
clamped_value = max(CacheManager.MIN_REVISIONS, value)
|
|
92
|
+
self._cache_manager.max_revisions = clamped_value
|
|
93
|
+
self.set_config_value("cache_max_revisions", clamped_value)
|
|
77
94
|
|
|
78
95
|
def get_config_value(self: Self, key: str, default: Any = None) -> Any:
|
|
79
96
|
"""Gets a configuration value by key.
|
|
@@ -230,15 +247,59 @@ class FMUDirectoryBase:
|
|
|
230
247
|
"""
|
|
231
248
|
return self.get_file_path(relative_path).exists()
|
|
232
249
|
|
|
250
|
+
def restore(self: Self) -> None:
|
|
251
|
+
"""Attempt to reconstruct missing .fmu files from in-memory state."""
|
|
252
|
+
if not self.path.exists():
|
|
253
|
+
self.path.mkdir(parents=True, exist_ok=True)
|
|
254
|
+
logger.info("Recreated missing .fmu directory at %s", self.path)
|
|
255
|
+
|
|
256
|
+
readme_path = self.get_file_path("README")
|
|
257
|
+
if self._README_CONTENT and not readme_path.exists():
|
|
258
|
+
self.write_text_file("README", self._README_CONTENT)
|
|
259
|
+
logger.info("Restored README at %s", readme_path)
|
|
260
|
+
|
|
261
|
+
config_path = self.config.path
|
|
262
|
+
if not config_path.exists():
|
|
263
|
+
cached_model = getattr(self.config, "_cache", None)
|
|
264
|
+
if cached_model is not None:
|
|
265
|
+
self.config.save(cached_model)
|
|
266
|
+
logger.info("Restored config.json from cached model at %s", config_path)
|
|
267
|
+
else:
|
|
268
|
+
self.config.reset()
|
|
269
|
+
logger.info("Restored config.json from defaults at %s", config_path)
|
|
270
|
+
|
|
233
271
|
|
|
234
272
|
class ProjectFMUDirectory(FMUDirectoryBase):
|
|
235
273
|
if TYPE_CHECKING:
|
|
236
274
|
config: ProjectConfigManager
|
|
237
275
|
|
|
238
|
-
|
|
239
|
-
|
|
276
|
+
_README_CONTENT: str = PROJECT_README_CONTENT
|
|
277
|
+
|
|
278
|
+
def __init__(
|
|
279
|
+
self: Self,
|
|
280
|
+
base_path: str | Path,
|
|
281
|
+
*,
|
|
282
|
+
lock_timeout_seconds: int = DEFAULT_LOCK_TIMEOUT,
|
|
283
|
+
) -> None:
|
|
284
|
+
"""Initializes a project-based .fmu directory.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
base_path: Project directory containing the .fmu folder.
|
|
288
|
+
lock_timeout_seconds: Lock expiration time in seconds. Default 20 minutes.
|
|
289
|
+
"""
|
|
240
290
|
self.config = ProjectConfigManager(self)
|
|
241
|
-
super().__init__(
|
|
291
|
+
super().__init__(
|
|
292
|
+
base_path,
|
|
293
|
+
CacheManager.MIN_REVISIONS,
|
|
294
|
+
lock_timeout_seconds=lock_timeout_seconds,
|
|
295
|
+
)
|
|
296
|
+
try:
|
|
297
|
+
max_revisions = self.config.get(
|
|
298
|
+
"cache_max_revisions", CacheManager.MIN_REVISIONS
|
|
299
|
+
)
|
|
300
|
+
self._cache_manager.max_revisions = max_revisions
|
|
301
|
+
except FileNotFoundError:
|
|
302
|
+
pass
|
|
242
303
|
|
|
243
304
|
def update_config(self: Self, updates: dict[str, Any]) -> ProjectConfig:
|
|
244
305
|
"""Updates multiple configuration values at once.
|
|
@@ -286,11 +347,17 @@ class ProjectFMUDirectory(FMUDirectoryBase):
|
|
|
286
347
|
return None
|
|
287
348
|
|
|
288
349
|
@classmethod
|
|
289
|
-
def find_nearest(
|
|
350
|
+
def find_nearest(
|
|
351
|
+
cls: type[Self],
|
|
352
|
+
start_path: str | Path = ".",
|
|
353
|
+
*,
|
|
354
|
+
lock_timeout_seconds: int = DEFAULT_LOCK_TIMEOUT,
|
|
355
|
+
) -> Self:
|
|
290
356
|
"""Factory method to find and open the nearest .fmu directory.
|
|
291
357
|
|
|
292
358
|
Args:
|
|
293
359
|
start_path: Path to start searching from. Default current working director
|
|
360
|
+
lock_timeout_seconds: Lock expiration time in seconds. Default 20 minutes.
|
|
294
361
|
|
|
295
362
|
Returns:
|
|
296
363
|
FMUDirectory instance
|
|
@@ -302,17 +369,38 @@ class ProjectFMUDirectory(FMUDirectoryBase):
|
|
|
302
369
|
fmu_dir_path = cls.find_fmu_directory(start_path)
|
|
303
370
|
if fmu_dir_path is None:
|
|
304
371
|
raise FileNotFoundError(f"No .fmu directory found at or above {start_path}")
|
|
305
|
-
return cls(fmu_dir_path.parent)
|
|
372
|
+
return cls(fmu_dir_path.parent, lock_timeout_seconds=lock_timeout_seconds)
|
|
306
373
|
|
|
307
374
|
|
|
308
375
|
class UserFMUDirectory(FMUDirectoryBase):
|
|
309
376
|
if TYPE_CHECKING:
|
|
310
377
|
config: UserConfigManager
|
|
311
378
|
|
|
312
|
-
|
|
313
|
-
|
|
379
|
+
_README_CONTENT: str = USER_README_CONTENT
|
|
380
|
+
|
|
381
|
+
def __init__(
|
|
382
|
+
self: Self,
|
|
383
|
+
*,
|
|
384
|
+
lock_timeout_seconds: int = DEFAULT_LOCK_TIMEOUT,
|
|
385
|
+
) -> None:
|
|
386
|
+
"""Initializes a user .fmu directory.
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
lock_timeout_seconds: Lock expiration time in seconds. Default 20 minutes.
|
|
390
|
+
"""
|
|
314
391
|
self.config = UserConfigManager(self)
|
|
315
|
-
super().__init__(
|
|
392
|
+
super().__init__(
|
|
393
|
+
Path.home(),
|
|
394
|
+
CacheManager.MIN_REVISIONS,
|
|
395
|
+
lock_timeout_seconds=lock_timeout_seconds,
|
|
396
|
+
)
|
|
397
|
+
try:
|
|
398
|
+
max_revisions = self.config.get(
|
|
399
|
+
"cache_max_revisions", CacheManager.MIN_REVISIONS
|
|
400
|
+
)
|
|
401
|
+
self._cache_manager.max_revisions = max_revisions
|
|
402
|
+
except FileNotFoundError:
|
|
403
|
+
pass
|
|
316
404
|
|
|
317
405
|
def update_config(self: Self, updates: dict[str, Any]) -> UserConfig:
|
|
318
406
|
"""Updates multiple configuration values at once.
|
|
@@ -330,12 +418,17 @@ class UserFMUDirectory(FMUDirectoryBase):
|
|
|
330
418
|
return cast("UserConfig", super().update_config(updates))
|
|
331
419
|
|
|
332
420
|
|
|
333
|
-
def get_fmu_directory(
|
|
421
|
+
def get_fmu_directory(
|
|
422
|
+
base_path: str | Path,
|
|
423
|
+
*,
|
|
424
|
+
lock_timeout_seconds: int = DEFAULT_LOCK_TIMEOUT,
|
|
425
|
+
) -> ProjectFMUDirectory:
|
|
334
426
|
"""Initializes access to a .fmu directory.
|
|
335
427
|
|
|
336
428
|
Args:
|
|
337
429
|
base_path: The directory containing the .fmu directory or one of its parent
|
|
338
430
|
dirs
|
|
431
|
+
lock_timeout_seconds: Lock expiration time in seconds. Default 20 minutes.
|
|
339
432
|
|
|
340
433
|
Returns:
|
|
341
434
|
FMUDirectory instance
|
|
@@ -346,14 +439,19 @@ def get_fmu_directory(base_path: str | Path) -> ProjectFMUDirectory:
|
|
|
346
439
|
PermissionError: If lacking permissions to read/write to the directory
|
|
347
440
|
|
|
348
441
|
"""
|
|
349
|
-
return ProjectFMUDirectory(base_path)
|
|
442
|
+
return ProjectFMUDirectory(base_path, lock_timeout_seconds=lock_timeout_seconds)
|
|
350
443
|
|
|
351
444
|
|
|
352
|
-
def find_nearest_fmu_directory(
|
|
445
|
+
def find_nearest_fmu_directory(
|
|
446
|
+
start_path: str | Path = ".",
|
|
447
|
+
*,
|
|
448
|
+
lock_timeout_seconds: int = DEFAULT_LOCK_TIMEOUT,
|
|
449
|
+
) -> ProjectFMUDirectory:
|
|
353
450
|
"""Factory method to find and open the nearest .fmu directory.
|
|
354
451
|
|
|
355
452
|
Args:
|
|
356
453
|
start_path: Path to start searching from. Default current working directory
|
|
454
|
+
lock_timeout_seconds: Lock expiration time in seconds. Default 20 minutes.
|
|
357
455
|
|
|
358
456
|
Returns:
|
|
359
457
|
FMUDirectory instance
|
|
@@ -361,4 +459,6 @@ def find_nearest_fmu_directory(start_path: str | Path = ".") -> ProjectFMUDirect
|
|
|
361
459
|
Raises:
|
|
362
460
|
FileNotFoundError: If no .fmu directory is found
|
|
363
461
|
"""
|
|
364
|
-
return ProjectFMUDirectory.find_nearest(
|
|
462
|
+
return ProjectFMUDirectory.find_nearest(
|
|
463
|
+
start_path, lock_timeout_seconds=lock_timeout_seconds
|
|
464
|
+
)
|
|
@@ -1,43 +1,18 @@
|
|
|
1
1
|
"""Initializes the .fmu directory."""
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from textwrap import dedent
|
|
5
4
|
from typing import Any, Final
|
|
6
5
|
|
|
7
6
|
from fmu.datamodels.fmu_results.global_configuration import GlobalConfiguration
|
|
8
7
|
|
|
9
8
|
from ._fmu_dir import ProjectFMUDirectory, UserFMUDirectory
|
|
10
9
|
from ._logging import null_logger
|
|
10
|
+
from ._readme_texts import PROJECT_README_CONTENT, USER_README_CONTENT
|
|
11
|
+
from ._resources.lock_manager import DEFAULT_LOCK_TIMEOUT
|
|
11
12
|
from .models.project_config import ProjectConfig
|
|
12
13
|
|
|
13
14
|
logger: Final = null_logger(__name__)
|
|
14
15
|
|
|
15
|
-
_README = dedent("""\
|
|
16
|
-
This directory contains static configuration data for your FMU project.
|
|
17
|
-
|
|
18
|
-
You should *not* manually modify files within this directory. Doing so may
|
|
19
|
-
result in erroneous behavior or erroneous data in your FMU project.
|
|
20
|
-
|
|
21
|
-
Changes to data stored within this directory must happen through the FMU
|
|
22
|
-
Settings application.
|
|
23
|
-
|
|
24
|
-
Run `fmu-settings` to do this.
|
|
25
|
-
""")
|
|
26
|
-
|
|
27
|
-
_USER_README = dedent("""\
|
|
28
|
-
This directory contains static data and configuration elements used by some
|
|
29
|
-
components in FMU. It may also contains sensitive access tokens that should not be
|
|
30
|
-
shared with others.
|
|
31
|
-
|
|
32
|
-
You should *not* manually modify files within this directory. Doing so may
|
|
33
|
-
result in erroneous behavior by some FMU components.
|
|
34
|
-
|
|
35
|
-
Changes to data stored within this directory must happen through the FMU
|
|
36
|
-
Settings application.
|
|
37
|
-
|
|
38
|
-
Run `fmu-settings` to do this.
|
|
39
|
-
""")
|
|
40
|
-
|
|
41
16
|
|
|
42
17
|
def _create_fmu_directory(base_path: Path) -> None:
|
|
43
18
|
"""Creates the .fmu directory.
|
|
@@ -71,6 +46,8 @@ def init_fmu_directory(
|
|
|
71
46
|
base_path: str | Path,
|
|
72
47
|
config_data: ProjectConfig | dict[str, Any] | None = None,
|
|
73
48
|
global_config: GlobalConfiguration | None = None,
|
|
49
|
+
*,
|
|
50
|
+
lock_timeout_seconds: int = DEFAULT_LOCK_TIMEOUT,
|
|
74
51
|
) -> ProjectFMUDirectory:
|
|
75
52
|
"""Creates and initializes a .fmu directory.
|
|
76
53
|
|
|
@@ -83,6 +60,7 @@ def init_fmu_directory(
|
|
|
83
60
|
data.
|
|
84
61
|
global_config: Optional GlobaConfiguration instance with existing global config
|
|
85
62
|
data.
|
|
63
|
+
lock_timeout_seconds: Lock expiration time in seconds. Default 20 minutes.
|
|
86
64
|
|
|
87
65
|
Returns:
|
|
88
66
|
Instance of FMUDirectory
|
|
@@ -98,8 +76,11 @@ def init_fmu_directory(
|
|
|
98
76
|
|
|
99
77
|
_create_fmu_directory(base_path)
|
|
100
78
|
|
|
101
|
-
fmu_dir = ProjectFMUDirectory(
|
|
102
|
-
|
|
79
|
+
fmu_dir = ProjectFMUDirectory(
|
|
80
|
+
base_path,
|
|
81
|
+
lock_timeout_seconds=lock_timeout_seconds,
|
|
82
|
+
)
|
|
83
|
+
fmu_dir.write_text_file("README", PROJECT_README_CONTENT)
|
|
103
84
|
|
|
104
85
|
fmu_dir.config.reset()
|
|
105
86
|
if config_data:
|
|
@@ -115,9 +96,15 @@ def init_fmu_directory(
|
|
|
115
96
|
return fmu_dir
|
|
116
97
|
|
|
117
98
|
|
|
118
|
-
def init_user_fmu_directory(
|
|
99
|
+
def init_user_fmu_directory(
|
|
100
|
+
*,
|
|
101
|
+
lock_timeout_seconds: int = DEFAULT_LOCK_TIMEOUT,
|
|
102
|
+
) -> UserFMUDirectory:
|
|
119
103
|
"""Creates and initializes a user's $HOME/.fmu directory.
|
|
120
104
|
|
|
105
|
+
Args:
|
|
106
|
+
lock_timeout_seconds: Lock expiration time in seconds. Default 20 minutes.
|
|
107
|
+
|
|
121
108
|
Returns:
|
|
122
109
|
Instance of FMUDirectory
|
|
123
110
|
|
|
@@ -131,8 +118,8 @@ def init_user_fmu_directory() -> UserFMUDirectory:
|
|
|
131
118
|
|
|
132
119
|
_create_fmu_directory(Path.home())
|
|
133
120
|
|
|
134
|
-
fmu_dir = UserFMUDirectory()
|
|
135
|
-
fmu_dir.write_text_file("README",
|
|
121
|
+
fmu_dir = UserFMUDirectory(lock_timeout_seconds=lock_timeout_seconds)
|
|
122
|
+
fmu_dir.write_text_file("README", USER_README_CONTENT)
|
|
136
123
|
|
|
137
124
|
fmu_dir.config.reset()
|
|
138
125
|
logger.debug(f"Successfully initialized .fmu directory at '{fmu_dir}'")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Shared README content for .fmu directories."""
|
|
2
|
+
|
|
3
|
+
from textwrap import dedent
|
|
4
|
+
from typing import Final
|
|
5
|
+
|
|
6
|
+
PROJECT_README_CONTENT: Final[str] = dedent(
|
|
7
|
+
"""\
|
|
8
|
+
This directory contains static configuration data for your FMU project.
|
|
9
|
+
|
|
10
|
+
You should *not* manually modify files within this directory. Doing so may
|
|
11
|
+
result in erroneous behavior or erroneous data in your FMU project.
|
|
12
|
+
|
|
13
|
+
Changes to data stored within this directory must happen through the FMU
|
|
14
|
+
Settings application.
|
|
15
|
+
|
|
16
|
+
Run `fmu-settings` to do this.
|
|
17
|
+
"""
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
USER_README_CONTENT: Final[str] = dedent(
|
|
21
|
+
"""\
|
|
22
|
+
This directory contains static data and configuration elements used by some
|
|
23
|
+
components in FMU. It may also contains sensitive access tokens that should not be
|
|
24
|
+
shared with others.
|
|
25
|
+
|
|
26
|
+
You should *not* manually modify files within this directory. Doing so may
|
|
27
|
+
result in erroneous behavior by some FMU components.
|
|
28
|
+
|
|
29
|
+
Changes to data stored within this directory must happen through the FMU
|
|
30
|
+
Settings application.
|
|
31
|
+
|
|
32
|
+
Run `fmu-settings` to do this.
|
|
33
|
+
"""
|
|
34
|
+
)
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from datetime import UTC, datetime
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import TYPE_CHECKING, Final, Self
|
|
7
|
+
from typing import TYPE_CHECKING, ClassVar, Final, Self
|
|
8
8
|
from uuid import uuid4
|
|
9
9
|
|
|
10
10
|
from fmu.settings._logging import null_logger
|
|
@@ -25,6 +25,8 @@ _CACHEDIR_TAG_CONTENT: Final = (
|
|
|
25
25
|
class CacheManager:
|
|
26
26
|
"""Stores complete file revisions under the `.fmu/cache` tree."""
|
|
27
27
|
|
|
28
|
+
MIN_REVISIONS: ClassVar[int] = 5
|
|
29
|
+
|
|
28
30
|
def __init__(
|
|
29
31
|
self: Self,
|
|
30
32
|
fmu_dir: FMUDirectoryBase,
|
|
@@ -35,10 +37,11 @@ class CacheManager:
|
|
|
35
37
|
Args:
|
|
36
38
|
fmu_dir: The FMUDirectory instance.
|
|
37
39
|
max_revisions: Maximum number of revisions to retain. Default is 5.
|
|
40
|
+
Values below 5 are set to 5.
|
|
38
41
|
"""
|
|
39
42
|
self._fmu_dir = fmu_dir
|
|
40
43
|
self._cache_root = Path("cache")
|
|
41
|
-
self._max_revisions = max(
|
|
44
|
+
self._max_revisions = max(self.MIN_REVISIONS, max_revisions)
|
|
42
45
|
|
|
43
46
|
@property
|
|
44
47
|
def max_revisions(self: Self) -> int:
|
|
@@ -47,8 +50,13 @@ class CacheManager:
|
|
|
47
50
|
|
|
48
51
|
@max_revisions.setter
|
|
49
52
|
def max_revisions(self: Self, value: int) -> None:
|
|
50
|
-
"""Update the per-resource revision retention.
|
|
51
|
-
|
|
53
|
+
"""Update the per-resource revision retention.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
value: The new maximum number of revisions. Minimum value is 5.
|
|
57
|
+
Values below 5 are set to 5.
|
|
58
|
+
"""
|
|
59
|
+
self._max_revisions = max(self.MIN_REVISIONS, value)
|
|
52
60
|
|
|
53
61
|
def store_revision(
|
|
54
62
|
self: Self,
|
|
@@ -65,12 +73,8 @@ class CacheManager:
|
|
|
65
73
|
encoding: Encoding used when persisting the snapshot. Defaults to UTF-8.
|
|
66
74
|
|
|
67
75
|
Returns:
|
|
68
|
-
Absolute filesystem path to the stored snapshot
|
|
69
|
-
disabled (``max_revisions`` equals zero).
|
|
76
|
+
Absolute filesystem path to the stored snapshot.
|
|
70
77
|
"""
|
|
71
|
-
if self.max_revisions == 0:
|
|
72
|
-
return None
|
|
73
|
-
|
|
74
78
|
resource_file_path = Path(resource_file_path)
|
|
75
79
|
cache_dir = self._ensure_resource_cache_dir(resource_file_path)
|
|
76
80
|
snapshot_name = self._snapshot_filename(resource_file_path)
|
|
@@ -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 = '0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.6.1'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 6, 1)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'gb2f5d6b1e'
|
|
@@ -23,6 +23,7 @@ class ProjectConfig(ResettableBaseModel):
|
|
|
23
23
|
masterdata: Masterdata | None = Field(default=None)
|
|
24
24
|
model: Model | None = Field(default=None)
|
|
25
25
|
access: Access | None = Field(default=None)
|
|
26
|
+
cache_max_revisions: int = Field(default=5, ge=5)
|
|
26
27
|
|
|
27
28
|
@classmethod
|
|
28
29
|
def reset(cls: type[Self]) -> Self:
|
|
@@ -38,4 +39,5 @@ class ProjectConfig(ResettableBaseModel):
|
|
|
38
39
|
masterdata=None,
|
|
39
40
|
model=None,
|
|
40
41
|
access=None,
|
|
42
|
+
cache_max_revisions=5,
|
|
41
43
|
)
|
|
@@ -10,6 +10,7 @@ import annotated_types
|
|
|
10
10
|
from pydantic import (
|
|
11
11
|
AwareDatetime,
|
|
12
12
|
BaseModel,
|
|
13
|
+
Field,
|
|
13
14
|
SecretStr,
|
|
14
15
|
field_serializer,
|
|
15
16
|
field_validator,
|
|
@@ -42,6 +43,7 @@ class UserConfig(ResettableBaseModel):
|
|
|
42
43
|
|
|
43
44
|
version: VersionStr
|
|
44
45
|
created_at: AwareDatetime
|
|
46
|
+
cache_max_revisions: int = Field(default=5, ge=5)
|
|
45
47
|
user_api_keys: UserAPIKeys
|
|
46
48
|
recent_project_directories: RecentProjectDirectories
|
|
47
49
|
|
|
@@ -51,6 +53,7 @@ class UserConfig(ResettableBaseModel):
|
|
|
51
53
|
return cls(
|
|
52
54
|
version=__version__,
|
|
53
55
|
created_at=datetime.now(UTC),
|
|
56
|
+
cache_max_revisions=5,
|
|
54
57
|
user_api_keys=UserAPIKeys(),
|
|
55
58
|
recent_project_directories=[],
|
|
56
59
|
)
|
|
@@ -39,6 +39,7 @@ def config_dict(unix_epoch_utc: datetime) -> dict[str, Any]:
|
|
|
39
39
|
"version": __version__,
|
|
40
40
|
"created_at": unix_epoch_utc,
|
|
41
41
|
"created_by": "user",
|
|
42
|
+
"cache_max_revisions": 5,
|
|
42
43
|
"masterdata": None,
|
|
43
44
|
"model": None,
|
|
44
45
|
"access": None,
|
|
@@ -283,6 +284,7 @@ def config_dict_with_masterdata(
|
|
|
283
284
|
"version": __version__,
|
|
284
285
|
"created_at": unix_epoch_utc,
|
|
285
286
|
"created_by": "user",
|
|
287
|
+
"cache_max_revisions": 5,
|
|
286
288
|
"masterdata": masterdata_dict,
|
|
287
289
|
"model": model_dict,
|
|
288
290
|
}
|
|
@@ -308,6 +310,7 @@ def user_config_dict(unix_epoch_utc: datetime) -> dict[str, Any]:
|
|
|
308
310
|
return {
|
|
309
311
|
"version": __version__,
|
|
310
312
|
"created_at": unix_epoch_utc,
|
|
313
|
+
"cache_max_revisions": 5,
|
|
311
314
|
"user_api_keys": {
|
|
312
315
|
"smda_subscription": None,
|
|
313
316
|
},
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""Tests for the ProjectFMUDirectory class."""
|
|
2
2
|
|
|
3
|
+
import inspect
|
|
3
4
|
import json
|
|
5
|
+
import shutil
|
|
4
6
|
from pathlib import Path
|
|
5
7
|
from unittest.mock import patch
|
|
6
8
|
|
|
@@ -8,8 +10,13 @@ import pytest
|
|
|
8
10
|
from pytest import MonkeyPatch
|
|
9
11
|
|
|
10
12
|
from fmu.settings import __version__, find_nearest_fmu_directory, get_fmu_directory
|
|
11
|
-
from fmu.settings._fmu_dir import
|
|
12
|
-
|
|
13
|
+
from fmu.settings._fmu_dir import (
|
|
14
|
+
FMUDirectoryBase,
|
|
15
|
+
ProjectFMUDirectory,
|
|
16
|
+
UserFMUDirectory,
|
|
17
|
+
)
|
|
18
|
+
from fmu.settings._readme_texts import PROJECT_README_CONTENT, USER_README_CONTENT
|
|
19
|
+
from fmu.settings._resources.lock_manager import DEFAULT_LOCK_TIMEOUT, LockManager
|
|
13
20
|
|
|
14
21
|
|
|
15
22
|
def test_init_existing_directory(fmu_dir: ProjectFMUDirectory) -> None:
|
|
@@ -390,3 +397,77 @@ def test_acquire_lock_on_user_fmu(
|
|
|
390
397
|
assert user_fmu_dir._lock.is_acquired()
|
|
391
398
|
assert user_fmu_dir._lock.exists
|
|
392
399
|
assert (user_fmu_dir.path / ".lock").exists()
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def test_restore_rebuilds_project_fmu_from_cache(
|
|
403
|
+
fmu_dir: ProjectFMUDirectory,
|
|
404
|
+
) -> None:
|
|
405
|
+
"""Tests that restore should recreate missing files using cached config data."""
|
|
406
|
+
fmu_dir.update_config({"version": "123.4.5"})
|
|
407
|
+
cached_dump = json.loads((fmu_dir.path / "config.json").read_text())
|
|
408
|
+
|
|
409
|
+
shutil.rmtree(fmu_dir.path)
|
|
410
|
+
assert not fmu_dir.path.exists()
|
|
411
|
+
|
|
412
|
+
fmu_dir.restore()
|
|
413
|
+
|
|
414
|
+
assert fmu_dir.path.exists()
|
|
415
|
+
readme_path = fmu_dir.path / "README"
|
|
416
|
+
assert readme_path.exists()
|
|
417
|
+
assert readme_path.read_text() == PROJECT_README_CONTENT
|
|
418
|
+
|
|
419
|
+
restored_dump = json.loads((fmu_dir.path / "config.json").read_text())
|
|
420
|
+
assert restored_dump == cached_dump
|
|
421
|
+
|
|
422
|
+
cache_dir = fmu_dir.path / "cache" / "config"
|
|
423
|
+
assert cache_dir.is_dir()
|
|
424
|
+
assert any(cache_dir.iterdir())
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def test_restore_resets_when_cache_missing(
|
|
428
|
+
fmu_dir: ProjectFMUDirectory,
|
|
429
|
+
) -> None:
|
|
430
|
+
"""Tests that restore should fall back to reset when no cached config exists."""
|
|
431
|
+
fmu_dir.config._cache = None
|
|
432
|
+
shutil.rmtree(fmu_dir.path)
|
|
433
|
+
assert not fmu_dir.path.exists()
|
|
434
|
+
|
|
435
|
+
with patch.object(
|
|
436
|
+
fmu_dir.config, "reset", wraps=fmu_dir.config.reset
|
|
437
|
+
) as mock_reset:
|
|
438
|
+
fmu_dir.restore()
|
|
439
|
+
|
|
440
|
+
mock_reset.assert_called_once()
|
|
441
|
+
assert fmu_dir.path.exists()
|
|
442
|
+
readme_path = fmu_dir.path / "README"
|
|
443
|
+
assert readme_path.exists()
|
|
444
|
+
assert readme_path.read_text() == PROJECT_README_CONTENT
|
|
445
|
+
assert (fmu_dir.config.path).exists()
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def test_restore_rebuilds_user_fmu(user_fmu_dir: UserFMUDirectory) -> None:
|
|
449
|
+
"""Tests that user FMU restore should recreate missing files using cached state."""
|
|
450
|
+
cached_dump = json.loads((user_fmu_dir.path / "config.json").read_text())
|
|
451
|
+
|
|
452
|
+
shutil.rmtree(user_fmu_dir.path)
|
|
453
|
+
assert not user_fmu_dir.path.exists()
|
|
454
|
+
|
|
455
|
+
user_fmu_dir.restore()
|
|
456
|
+
|
|
457
|
+
assert user_fmu_dir.path.exists()
|
|
458
|
+
readme_path = user_fmu_dir.path / "README"
|
|
459
|
+
assert readme_path.exists()
|
|
460
|
+
assert readme_path.read_text() == USER_README_CONTENT
|
|
461
|
+
|
|
462
|
+
restored_dump = json.loads((user_fmu_dir.path / "config.json").read_text())
|
|
463
|
+
assert restored_dump == cached_dump
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def test_fmu_directory_base_exposes_lock_timeout_kwarg() -> None:
|
|
467
|
+
"""Tests that the kw-only lock timeout argument remains available."""
|
|
468
|
+
signature = inspect.signature(FMUDirectoryBase.__init__)
|
|
469
|
+
lock_timeout = signature.parameters.get("lock_timeout_seconds")
|
|
470
|
+
|
|
471
|
+
assert lock_timeout is not None, "lock_timeout_seconds kwarg missing from base init"
|
|
472
|
+
assert lock_timeout.kind is inspect.Parameter.KEYWORD_ONLY
|
|
473
|
+
assert lock_timeout.default == DEFAULT_LOCK_TIMEOUT
|
|
@@ -11,12 +11,11 @@ import pytest
|
|
|
11
11
|
from fmu.settings import __version__
|
|
12
12
|
from fmu.settings._global_config import find_global_config
|
|
13
13
|
from fmu.settings._init import (
|
|
14
|
-
_README,
|
|
15
|
-
_USER_README,
|
|
16
14
|
_create_fmu_directory,
|
|
17
15
|
init_fmu_directory,
|
|
18
16
|
init_user_fmu_directory,
|
|
19
17
|
)
|
|
18
|
+
from fmu.settings._readme_texts import PROJECT_README_CONTENT, USER_README_CONTENT
|
|
20
19
|
from fmu.settings.models.project_config import ProjectConfig
|
|
21
20
|
from fmu.settings.models.user_config import UserConfig
|
|
22
21
|
|
|
@@ -128,7 +127,7 @@ def test_readme_is_written(tmp_path: Path, config_model: ProjectConfig) -> None:
|
|
|
128
127
|
|
|
129
128
|
readme = fmu_dir.path / "README"
|
|
130
129
|
assert readme.exists()
|
|
131
|
-
assert readme.read_text() ==
|
|
130
|
+
assert readme.read_text() == PROJECT_README_CONTENT
|
|
132
131
|
|
|
133
132
|
|
|
134
133
|
def test_init_user_fmu_directory(
|
|
@@ -178,4 +177,4 @@ def test_user_readme_is_written(tmp_path: Path, config_model: ProjectConfig) ->
|
|
|
178
177
|
|
|
179
178
|
readme = fmu_dir.path / "README"
|
|
180
179
|
assert readme.exists()
|
|
181
|
-
assert readme.read_text() ==
|
|
180
|
+
assert readme.read_text() == USER_README_CONTENT
|
|
@@ -5,7 +5,10 @@ from __future__ import annotations
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
|
-
from fmu.settings._resources.cache_manager import
|
|
8
|
+
from fmu.settings._resources.cache_manager import (
|
|
9
|
+
_CACHEDIR_TAG_CONTENT,
|
|
10
|
+
CacheManager,
|
|
11
|
+
)
|
|
9
12
|
|
|
10
13
|
if TYPE_CHECKING:
|
|
11
14
|
import pytest
|
|
@@ -84,8 +87,9 @@ def test_cache_manager_trim_handles_missing_files(
|
|
|
84
87
|
monkeypatch: pytest.MonkeyPatch,
|
|
85
88
|
) -> None:
|
|
86
89
|
"""Trimming gracefully handles concurrent removals."""
|
|
87
|
-
manager = CacheManager(fmu_dir, max_revisions=
|
|
88
|
-
|
|
90
|
+
manager = CacheManager(fmu_dir, max_revisions=CacheManager.MIN_REVISIONS)
|
|
91
|
+
for i in range(CacheManager.MIN_REVISIONS + 2):
|
|
92
|
+
manager.store_revision("foo.json", f"content_{i}")
|
|
89
93
|
|
|
90
94
|
original_unlink = Path.unlink
|
|
91
95
|
|
|
@@ -98,19 +102,7 @@ def test_cache_manager_trim_handles_missing_files(
|
|
|
98
102
|
|
|
99
103
|
monkeypatch.setattr(Path, "unlink", flaky_unlink)
|
|
100
104
|
|
|
101
|
-
manager.store_revision("foo.json", "
|
|
105
|
+
manager.store_revision("foo.json", "final")
|
|
102
106
|
|
|
103
107
|
config_cache = fmu_dir.path / "cache" / "foo"
|
|
104
|
-
assert
|
|
105
|
-
assert len(_read_snapshot_names(config_cache)) == 1
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def test_cache_manager_max_revisions_zero_skips_storage(
|
|
109
|
-
fmu_dir: ProjectFMUDirectory,
|
|
110
|
-
) -> None:
|
|
111
|
-
"""Storing with zero retention should return None and create nothing."""
|
|
112
|
-
manager = CacheManager(fmu_dir, max_revisions=0)
|
|
113
|
-
result = manager.store_revision("foo.json", "data")
|
|
114
|
-
assert result is None
|
|
115
|
-
cache_dir = fmu_dir.path / "cache" / "foo"
|
|
116
|
-
assert not cache_dir.exists()
|
|
108
|
+
assert len(_read_snapshot_names(config_cache)) == CacheManager.MIN_REVISIONS
|
|
@@ -523,6 +523,24 @@ def test_refresh_without_lock_file(
|
|
|
523
523
|
assert fmu_dir._lock.is_acquired() is False
|
|
524
524
|
|
|
525
525
|
|
|
526
|
+
def test_refresh_missing_lock_releases_owned_lock(
|
|
527
|
+
fmu_dir: ProjectFMUDirectory,
|
|
528
|
+
) -> None:
|
|
529
|
+
"""Tests refresh releases cached state when lock file is missing."""
|
|
530
|
+
lock = LockManager(fmu_dir)
|
|
531
|
+
lock.acquire()
|
|
532
|
+
lock.path.unlink()
|
|
533
|
+
|
|
534
|
+
with (
|
|
535
|
+
patch.object(lock, "is_acquired", return_value=True),
|
|
536
|
+
patch.object(lock, "release") as mock_release,
|
|
537
|
+
pytest.raises(LockNotFoundError, match="lock file does not exist"),
|
|
538
|
+
):
|
|
539
|
+
lock.refresh()
|
|
540
|
+
|
|
541
|
+
mock_release.assert_called_once()
|
|
542
|
+
|
|
543
|
+
|
|
526
544
|
def test_refresh_without_owning_lock(
|
|
527
545
|
fmu_dir: ProjectFMUDirectory, monkeypatch: MonkeyPatch
|
|
528
546
|
) -> None:
|
|
@@ -10,7 +10,6 @@ import pytest
|
|
|
10
10
|
from pydantic import BaseModel
|
|
11
11
|
|
|
12
12
|
from fmu.settings._fmu_dir import ProjectFMUDirectory
|
|
13
|
-
from fmu.settings._resources.cache_manager import CacheManager
|
|
14
13
|
from fmu.settings._resources.lock_manager import LockManager
|
|
15
14
|
from fmu.settings._resources.pydantic_resource_manager import PydanticResourceManager
|
|
16
15
|
|
|
@@ -230,57 +229,28 @@ def test_pydantic_resource_manager_save_stores_revision_when_enabled(
|
|
|
230
229
|
|
|
231
230
|
|
|
232
231
|
def test_pydantic_resource_manager_revision_cache_trims_excess(
|
|
233
|
-
fmu_dir: ProjectFMUDirectory, monkeypatch: pytest.MonkeyPatch
|
|
234
|
-
) -> None:
|
|
235
|
-
"""Revision caching should retain only the configured number of snapshots."""
|
|
236
|
-
original_limit = fmu_dir.cache_max_revisions
|
|
237
|
-
fmu_dir.cache_max_revisions = 2
|
|
238
|
-
try:
|
|
239
|
-
sequence = iter(["rev1.json", "rev2.json", "rev3.json"])
|
|
240
|
-
monkeypatch.setattr(
|
|
241
|
-
CacheManager,
|
|
242
|
-
"_snapshot_filename",
|
|
243
|
-
lambda self, config_file_path: next(sequence),
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
a = AManager(fmu_dir)
|
|
247
|
-
a.save(A(foo="one"))
|
|
248
|
-
a.save(A(foo="two"))
|
|
249
|
-
a.save(A(foo="three"))
|
|
250
|
-
finally:
|
|
251
|
-
fmu_dir.cache_max_revisions = original_limit
|
|
252
|
-
|
|
253
|
-
config_cache = fmu_dir.path / "cache" / "foo"
|
|
254
|
-
snapshots = sorted(p.name for p in config_cache.iterdir())
|
|
255
|
-
assert snapshots == ["rev2.json", "rev3.json"]
|
|
256
|
-
|
|
257
|
-
assert (
|
|
258
|
-
json.loads((config_cache / "rev3.json").read_text(encoding="utf-8"))["foo"]
|
|
259
|
-
== "three"
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
def test_pydantic_resource_manager_respects_retention_setting(
|
|
264
232
|
fmu_dir: ProjectFMUDirectory,
|
|
265
233
|
) -> None:
|
|
266
|
-
"""
|
|
234
|
+
"""Revision caching should retain only the configured number of snapshots."""
|
|
267
235
|
original_limit = fmu_dir.cache_max_revisions
|
|
268
|
-
fmu_dir.cache_max_revisions =
|
|
236
|
+
fmu_dir.cache_max_revisions = 5
|
|
269
237
|
try:
|
|
270
238
|
a = AManager(fmu_dir)
|
|
271
239
|
a.save(A(foo="one"))
|
|
272
240
|
a.save(A(foo="two"))
|
|
273
241
|
a.save(A(foo="three"))
|
|
274
242
|
a.save(A(foo="four"))
|
|
243
|
+
a.save(A(foo="five"))
|
|
244
|
+
a.save(A(foo="six"))
|
|
275
245
|
finally:
|
|
276
246
|
fmu_dir.cache_max_revisions = original_limit
|
|
277
247
|
|
|
278
248
|
config_cache = fmu_dir.path / "cache" / "foo"
|
|
279
249
|
snapshots = sorted(p.name for p in config_cache.iterdir())
|
|
280
|
-
assert len(snapshots) ==
|
|
250
|
+
assert len(snapshots) == 5 # noqa: PLR2004
|
|
281
251
|
|
|
282
252
|
contents = [
|
|
283
253
|
json.loads((config_cache / name).read_text(encoding="utf-8"))["foo"]
|
|
284
254
|
for name in snapshots
|
|
285
255
|
]
|
|
286
|
-
assert contents == ["two", "three", "four"]
|
|
256
|
+
assert contents == ["two", "three", "four", "five", "six"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fmu_settings-0.5.4 → fmu_settings-0.6.1}/src/fmu/settings/_resources/pydantic_resource_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|