meerschaum 3.0.0rc4__py3-none-any.whl → 3.0.0rc7__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 (117) hide show
  1. meerschaum/_internal/arguments/_parser.py +14 -2
  2. meerschaum/_internal/cli/__init__.py +6 -0
  3. meerschaum/_internal/cli/daemons.py +103 -0
  4. meerschaum/_internal/cli/entry.py +220 -0
  5. meerschaum/_internal/cli/workers.py +434 -0
  6. meerschaum/_internal/docs/index.py +1 -2
  7. meerschaum/_internal/entry.py +44 -8
  8. meerschaum/_internal/shell/Shell.py +113 -19
  9. meerschaum/_internal/shell/__init__.py +4 -1
  10. meerschaum/_internal/static.py +3 -1
  11. meerschaum/_internal/term/TermPageHandler.py +1 -2
  12. meerschaum/_internal/term/__init__.py +40 -6
  13. meerschaum/_internal/term/tools.py +33 -8
  14. meerschaum/actions/__init__.py +6 -4
  15. meerschaum/actions/api.py +39 -11
  16. meerschaum/actions/attach.py +1 -0
  17. meerschaum/actions/delete.py +4 -2
  18. meerschaum/actions/edit.py +27 -8
  19. meerschaum/actions/login.py +8 -8
  20. meerschaum/actions/register.py +13 -7
  21. meerschaum/actions/reload.py +22 -5
  22. meerschaum/actions/restart.py +14 -0
  23. meerschaum/actions/show.py +69 -4
  24. meerschaum/actions/start.py +135 -14
  25. meerschaum/actions/stop.py +36 -3
  26. meerschaum/actions/sync.py +6 -1
  27. meerschaum/api/__init__.py +35 -13
  28. meerschaum/api/_events.py +2 -2
  29. meerschaum/api/_oauth2.py +47 -4
  30. meerschaum/api/dash/callbacks/dashboard.py +29 -0
  31. meerschaum/api/dash/callbacks/jobs.py +3 -2
  32. meerschaum/api/dash/callbacks/login.py +10 -1
  33. meerschaum/api/dash/callbacks/register.py +9 -2
  34. meerschaum/api/dash/pages/login.py +2 -2
  35. meerschaum/api/dash/pipes.py +72 -36
  36. meerschaum/api/dash/webterm.py +14 -6
  37. meerschaum/api/models/_pipes.py +7 -1
  38. meerschaum/api/resources/static/js/terminado.js +3 -0
  39. meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
  40. meerschaum/api/resources/templates/termpage.html +1 -0
  41. meerschaum/api/routes/_jobs.py +23 -11
  42. meerschaum/api/routes/_login.py +73 -5
  43. meerschaum/api/routes/_pipes.py +6 -4
  44. meerschaum/api/routes/_webterm.py +3 -3
  45. meerschaum/config/__init__.py +60 -13
  46. meerschaum/config/_default.py +89 -61
  47. meerschaum/config/_edit.py +10 -8
  48. meerschaum/config/_formatting.py +2 -0
  49. meerschaum/config/_patch.py +4 -2
  50. meerschaum/config/_paths.py +127 -12
  51. meerschaum/config/_read_config.py +20 -10
  52. meerschaum/config/_version.py +1 -1
  53. meerschaum/config/environment.py +262 -0
  54. meerschaum/config/stack/__init__.py +7 -5
  55. meerschaum/connectors/_Connector.py +1 -2
  56. meerschaum/connectors/__init__.py +37 -2
  57. meerschaum/connectors/api/_APIConnector.py +1 -1
  58. meerschaum/connectors/api/_jobs.py +11 -0
  59. meerschaum/connectors/api/_pipes.py +7 -1
  60. meerschaum/connectors/instance/_plugins.py +9 -1
  61. meerschaum/connectors/instance/_tokens.py +20 -3
  62. meerschaum/connectors/instance/_users.py +8 -1
  63. meerschaum/connectors/parse.py +1 -1
  64. meerschaum/connectors/sql/_create_engine.py +3 -0
  65. meerschaum/connectors/sql/_pipes.py +93 -79
  66. meerschaum/connectors/sql/_users.py +8 -1
  67. meerschaum/connectors/valkey/_ValkeyConnector.py +3 -3
  68. meerschaum/connectors/valkey/_pipes.py +7 -5
  69. meerschaum/core/Pipe/__init__.py +45 -71
  70. meerschaum/core/Pipe/_attributes.py +66 -90
  71. meerschaum/core/Pipe/_cache.py +555 -0
  72. meerschaum/core/Pipe/_clear.py +0 -11
  73. meerschaum/core/Pipe/_data.py +0 -50
  74. meerschaum/core/Pipe/_deduplicate.py +0 -13
  75. meerschaum/core/Pipe/_delete.py +12 -21
  76. meerschaum/core/Pipe/_drop.py +11 -23
  77. meerschaum/core/Pipe/_dtypes.py +1 -1
  78. meerschaum/core/Pipe/_index.py +8 -14
  79. meerschaum/core/Pipe/_sync.py +12 -18
  80. meerschaum/core/Plugin/_Plugin.py +7 -1
  81. meerschaum/core/Token/_Token.py +1 -1
  82. meerschaum/core/User/_User.py +1 -2
  83. meerschaum/jobs/_Executor.py +88 -4
  84. meerschaum/jobs/_Job.py +135 -35
  85. meerschaum/jobs/systemd.py +7 -2
  86. meerschaum/plugins/__init__.py +277 -81
  87. meerschaum/utils/daemon/Daemon.py +195 -41
  88. meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
  89. meerschaum/utils/daemon/RotatingFile.py +63 -36
  90. meerschaum/utils/daemon/StdinFile.py +53 -13
  91. meerschaum/utils/daemon/__init__.py +18 -5
  92. meerschaum/utils/daemon/_names.py +6 -3
  93. meerschaum/utils/debug.py +34 -4
  94. meerschaum/utils/dtypes/__init__.py +5 -1
  95. meerschaum/utils/formatting/__init__.py +4 -1
  96. meerschaum/utils/formatting/_jobs.py +1 -1
  97. meerschaum/utils/formatting/_pipes.py +47 -46
  98. meerschaum/utils/formatting/_shell.py +16 -6
  99. meerschaum/utils/misc.py +18 -38
  100. meerschaum/utils/packages/__init__.py +15 -13
  101. meerschaum/utils/packages/_packages.py +1 -0
  102. meerschaum/utils/pipes.py +33 -5
  103. meerschaum/utils/process.py +1 -1
  104. meerschaum/utils/prompt.py +171 -144
  105. meerschaum/utils/sql.py +12 -2
  106. meerschaum/utils/threading.py +42 -0
  107. meerschaum/utils/venv/__init__.py +2 -0
  108. meerschaum/utils/warnings.py +19 -13
  109. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/METADATA +3 -1
  110. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/RECORD +116 -110
  111. meerschaum/config/_environment.py +0 -145
  112. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/WHEEL +0 -0
  113. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/entry_points.txt +0 -0
  114. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/licenses/LICENSE +0 -0
  115. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/licenses/NOTICE +0 -0
  116. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/top_level.txt +0 -0
  117. {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc7.dist-info}/zip-safe +0 -0
@@ -36,8 +36,7 @@ CONNECTOR_ATTRIBUTES: Dict[str, Dict[str, Any]] = {
36
36
  default_meerschaum_config = {
37
37
  'instance': 'sql:main',
38
38
  'api_instance': 'MRSM{meerschaum:instance}',
39
- 'web_instance': 'MRSM{meerschaum:instance}',
40
- 'default_repository': 'api:mrsm',
39
+ 'repository': 'api:mrsm',
41
40
  'connectors': {
42
41
  'sql': {
43
42
  'default': {},
@@ -51,7 +50,7 @@ default_meerschaum_config = {
51
50
  },
52
51
  'local': {
53
52
  'flavor': 'sqlite',
54
- 'database': SQLITE_DB_PATH.as_posix(),
53
+ 'database': "{SQLITE_DB_PATH}",
55
54
  },
56
55
  'memory': {
57
56
  'flavor': 'sqlite',
@@ -117,75 +116,107 @@ default_system_config = {
117
116
  'connect_args': {},
118
117
  },
119
118
  },
120
-
121
119
  'api': {
122
120
  },
123
121
  },
124
- ### not to be confused with system_config['connectors']['api'], this is the configuration
125
- ### for the API server itself.
126
- 'api': {
127
- 'uvicorn': {
128
- 'app': 'meerschaum.api:app',
129
- 'port': 8000,
130
- 'host': '0.0.0.0',
131
- 'workers': max(int(multiprocessing.cpu_count() / 2), 1),
132
- 'proxy_headers': True,
133
- 'forwarded_allow_ips': '*',
134
- },
135
- 'cache': {
136
- 'connector': 'valkey:main',
137
- 'session_expires_minutes': 43200,
122
+ 'api': 'MRSM{api}',
123
+ 'webterm': 'MRSM{api:webterm}',
124
+ 'cli': {
125
+ 'max_daemons': (multiprocessing.cpu_count() * 3),
126
+ 'refresh_seconds': 0.1,
127
+ 'allowed_prefixes': ['*'],
128
+ 'disallowed_prefixes': [
129
+ 'edit',
130
+ 'start daemon',
131
+ 'start job',
132
+ 'stop daemon',
133
+ 'show daemon',
134
+ 'restart daemon',
135
+ 'reload',
136
+ 'start worker',
137
+ 'start job',
138
+ 'python',
139
+ 'login',
140
+ 'executor',
141
+ 'os ',
142
+ 'sh ',
143
+ 'start api',
144
+ 'start webterm',
145
+ 'stack',
146
+ 'instance',
147
+ 'debug',
148
+ 'bootstrap',
149
+ 'daemon',
150
+ ],
151
+ },
152
+ 'experimental': {
153
+ 'fetch': False,
154
+ 'cache': True,
155
+ 'space': False,
156
+ 'join_fetch': False,
157
+ 'inplace_sync': True,
158
+ 'uv_pip': True,
159
+ 'systemd_healthcheck': False,
160
+ 'valkey_session_cache': True,
161
+ 'cli_daemon': True,
162
+ },
163
+ }
164
+
165
+ default_api_config = {
166
+ 'uvicorn': {
167
+ 'app': 'meerschaum.api:app',
168
+ 'port': 8000,
169
+ 'host': '0.0.0.0',
170
+ 'workers': max(int(multiprocessing.cpu_count() / 2), 1),
171
+ 'proxy_headers': True,
172
+ 'forwarded_allow_ips': '*',
173
+ },
174
+ 'cache': {
175
+ 'connector': 'valkey:main',
176
+ 'session_expires_minutes': 43200,
177
+ },
178
+ 'data': {
179
+ 'max_response_row_limit': 100_000,
180
+ 'chunks': {
181
+ 'ttl_seconds': 1800,
138
182
  },
139
- 'data': {
140
- 'max_response_row_limit': 100_000,
141
- 'chunks': {
142
- 'ttl_seconds': 1800,
143
- },
183
+ },
184
+ 'endpoints': {
185
+ 'docs_in_production': True,
186
+ },
187
+ 'tokens': {
188
+ 'valid_refresh_minutes': 60,
189
+ 'default_expiration_days': 366,
190
+ },
191
+ 'permissions': {
192
+ 'registration': {
193
+ 'users': True,
194
+ 'pipes': True,
195
+ 'plugins': True,
144
196
  },
145
- 'endpoints': {
146
- 'docs_in_production': True,
197
+ 'actions': {
198
+ 'non_admin': True,
147
199
  },
148
- 'tokens': {
149
- 'valid_refresh_minutes': 60,
150
- 'default_expiration_days': 366,
200
+ 'chaining': {
201
+ 'insecure_parent_instance': False,
202
+ 'child_apis': False,
151
203
  },
152
- 'permissions': {
153
- 'registration': {
154
- 'users': True,
155
- 'pipes': True,
156
- 'plugins': True,
157
- },
158
- 'actions': {
159
- 'non_admin': True,
160
- },
161
- 'chaining': {
162
- 'insecure_parent_instance': False,
163
- 'child_apis': False,
164
- },
165
- 'instances': {
166
- 'allow_multiple_instances': True,
167
- 'allowed_instance_keys': ['*']
168
- },
204
+ 'instances': {
205
+ 'allow_multiple_instances': True,
206
+ 'allowed_instance_keys': ['*']
169
207
  },
170
- 'protocol': default_meerschaum_config['connectors']['api']['default']['protocol'],
171
208
  },
209
+ 'protocol': default_meerschaum_config['connectors']['api']['default']['protocol'],
172
210
  'webterm': {
173
211
  'tmux': {
174
212
  'enabled': True,
175
213
  'session_suffix': '_mrsm',
176
214
  },
177
- },
178
- 'experimental': {
179
- 'fetch': False,
180
- 'cache': True,
181
- 'space': False,
182
- 'join_fetch': False,
183
- 'inplace_sync': True,
184
- 'uv_pip': True,
185
- 'systemd_healthcheck': False,
186
- 'valkey_session_cache': True,
215
+ 'host': '127.0.0.1',
216
+ 'port': 8765,
187
217
  },
188
218
  }
219
+
189
220
  default_pipes_config = {
190
221
  'parameters': {
191
222
  'columns': {
@@ -221,15 +252,12 @@ default_pipes_config = {
221
252
  },
222
253
  }
223
254
  default_plugins_config = {}
224
- default_experimental_config = {
225
- 'venv': True,
226
- }
227
-
228
255
 
229
256
  ### build default config dictionary
230
257
  default_config = {}
231
258
  default_config['meerschaum'] = default_meerschaum_config
232
259
  default_config['system'] = default_system_config
260
+ default_config['api'] = default_api_config
233
261
  from meerschaum.config._formatting import default_formatting_config
234
262
  default_config['formatting'] = default_formatting_config
235
263
  from meerschaum.config._shell import default_shell_config
@@ -241,7 +269,7 @@ default_config['jobs'] = default_jobs_config
241
269
  ### add configs from other packages
242
270
  try:
243
271
  import meerschaum.config.stack
244
- except ImportError as e:
272
+ except ImportError:
245
273
  pass
246
274
  finally:
247
275
  from meerschaum.config.stack import default_stack_config
@@ -103,19 +103,23 @@ def write_config(
103
103
  if directory is None:
104
104
  from meerschaum.config._paths import CONFIG_DIR_PATH
105
105
  directory = CONFIG_DIR_PATH
106
+
107
+ from meerschaum.config import _allow_write_missing
106
108
  from meerschaum._internal.static import STATIC_CONFIG
107
109
  from meerschaum.config._default import default_header_comment
108
- from meerschaum.config._patch import apply_patch_to_config
109
- from meerschaum.config._read_config import get_keyfile_path
110
- from meerschaum.utils.debug import dprint
110
+ from meerschaum.config._read_config import get_keyfile_path, revert_symlinks_config
111
111
  from meerschaum.utils.yaml import yaml
112
112
  from meerschaum.utils.misc import filter_keywords
113
- import json, os
113
+ import json
114
+ import os
114
115
  if config_dict is None:
115
116
  from meerschaum.config import _config
116
- cf = _config()
117
+ cf = _config(allow_replaced=False)
117
118
  config_dict = cf
118
119
 
120
+ if not _allow_write_missing:
121
+ return False
122
+
119
123
  default_filetype = STATIC_CONFIG['config']['default_filetype']
120
124
  filetype_dumpers = {
121
125
  'yml' : yaml.dump,
@@ -123,9 +127,7 @@ def write_config(
123
127
  'json' : json.dump,
124
128
  }
125
129
 
126
- symlinks_key = STATIC_CONFIG['config']['symlinks_key']
127
- symlinks = config_dict.pop(symlinks_key) if symlinks_key in config_dict else {}
128
- config_dict = apply_patch_to_config(config_dict, symlinks)
130
+ config_dict = revert_symlinks_config(config_dict)
129
131
 
130
132
  def determine_filetype(k, v):
131
133
  if k == 'meerschaum':
@@ -28,6 +28,8 @@ default_formatting_config = {
28
28
  'connector' : '🔌',
29
29
  'metric' : '📊',
30
30
  'location' : '📍',
31
+ 'locked' : '🔒',
32
+ 'unlocked' : '🔓',
31
33
  'key' : '🔑',
32
34
  'idea' : '💡',
33
35
  'connected' : '🟢',
@@ -9,11 +9,12 @@ Functions for patching the configuration dictionary
9
9
  import sys
10
10
  import copy
11
11
  from meerschaum.utils.typing import Dict, Any
12
- from meerschaum.utils.warnings import warn
12
+ from meerschaum.utils.warnings import warn as _warn
13
13
 
14
14
  def apply_patch_to_config(
15
15
  config: Dict[str, Any],
16
16
  patch: Dict[str, Any],
17
+ warn: bool = False,
17
18
  ) -> Dict[str, Any]:
18
19
  """Patch the config dict with a new dict (cascade patching)."""
19
20
  _base = copy.deepcopy(config) if isinstance(config, dict) else {}
@@ -24,7 +25,8 @@ def apply_patch_to_config(
24
25
  if base is None:
25
26
  return {}
26
27
  if not isinstance(base, dict):
27
- warn(f"Overwriting the value {base} with a dictionary:\n{patch}")
28
+ if warn:
29
+ _warn(f"Overwriting the value {base} with a dictionary:\n{patch}")
28
30
  base = {}
29
31
  for key, value in patch.items():
30
32
  if isinstance(value, dict):
@@ -9,10 +9,17 @@ Define file paths
9
9
  from __future__ import annotations
10
10
 
11
11
  from pathlib import Path
12
- import os, platform, sys, json
13
- from meerschaum.utils.typing import Union
12
+ import contextlib
13
+ import os
14
+ import platform
15
+ import sys
16
+ import json
17
+
18
+ from typing import Union, List
19
+
14
20
  from meerschaum._internal.static import STATIC_CONFIG
15
21
 
22
+
16
23
  DOT_CONFIG_DIR_PATH = Path(
17
24
  os.environ.get('XDG_CONFIG_HOME', Path.home() / '.config')
18
25
  if platform.system() != 'Windows'
@@ -71,7 +78,7 @@ if ENVIRONMENT_PLUGINS_DIR in os.environ:
71
78
  if path_str
72
79
  ]
73
80
  )
74
- except Exception as e:
81
+ except Exception:
75
82
  PLUGINS_DIR_PATHS = []
76
83
 
77
84
  if not PLUGINS_DIR_PATHS:
@@ -82,20 +89,23 @@ if ENVIRONMENT_PLUGINS_DIR in os.environ:
82
89
  f"`export {ENVIRONMENT_PLUGINS_DIR}=./plugins:/another/path/to/plugins`\n\n"
83
90
  "or a JSON-encoded path list:\n\n"
84
91
  f"`export {ENVIRONMENT_PLUGINS_DIR}=" + "'[\"./plugins\", \"/another/path/to/plugins\"]'`"
85
- f"",
92
+ "",
86
93
  )
87
94
  sys.exit(1)
88
95
  else:
89
96
  PLUGINS_DIR_PATHS = [_ROOT_DIR_PATH / 'plugins']
90
97
 
91
98
  ### Remove duplicate plugins paths.
92
- _seen_plugins_paths, _plugins_paths_to_remove = set(), set()
93
- for _plugin_path in PLUGINS_DIR_PATHS:
94
- if _plugin_path in _seen_plugins_paths:
95
- _plugins_paths_to_remove.add(_plugin_path)
96
- _seen_plugins_paths.add(_plugin_path)
97
- for _plugin_path in _plugins_paths_to_remove:
98
- PLUGINS_DIR_PATHS.remove(_plugin_path)
99
+ def _process_plugins_dir_paths(plugins_dir_paths: List[Path]):
100
+ _seen_plugins_paths, _plugins_paths_to_remove = set(), set()
101
+ for _plugin_path in plugins_dir_paths:
102
+ if _plugin_path in _seen_plugins_paths:
103
+ _plugins_paths_to_remove.add(_plugin_path)
104
+ _seen_plugins_paths.add(_plugin_path)
105
+ for _plugin_path in _plugins_paths_to_remove:
106
+ plugins_dir_paths.remove(_plugin_path)
107
+ _process_plugins_dir_paths(PLUGINS_DIR_PATHS)
108
+
99
109
 
100
110
  ENVIRONMENT_VENVS_DIR = STATIC_CONFIG['environment']['venvs']
101
111
  if ENVIRONMENT_VENVS_DIR in os.environ:
@@ -144,6 +154,8 @@ paths = {
144
154
  'API_SECRET_KEY_PATH' : ('{API_CONFIG_RESOURCES_PATH}', '.api_secret_key'),
145
155
  'API_UVICORN_RESOURCES_PATH' : ('{API_CONFIG_RESOURCES_PATH}', 'uvicorn'),
146
156
  'API_UVICORN_CONFIG_PATH' : ('{API_UVICORN_RESOURCES_PATH}', '.thread_config.json'),
157
+
158
+ 'WEBTERM_INTERNAL_RESOURCES_PATH': ('{INTERNAL_RESOURCES_PATH}', 'webterm'),
147
159
 
148
160
  'CACHE_RESOURCES_PATH' : ('{ROOT_DIR_PATH}', '.cache'),
149
161
  'PIPES_CACHE_RESOURCES_PATH' : ('{CACHE_RESOURCES_PATH}', 'pipes'),
@@ -151,12 +163,16 @@ paths = {
151
163
  'VENVS_CACHE_RESOURCES_PATH' : ('{CACHE_RESOURCES_PATH}', 'venvs'),
152
164
  'SQL_CONN_CACHE_RESOURCES_PATH' : ('{CACHE_RESOURCES_PATH}', 'sql'),
153
165
 
166
+ 'CLI_RESOURCES_PATH' : ('{INTERNAL_RESOURCES_PATH}', 'cli'),
167
+ 'CLI_LOGS_RESOURCES_PATH' : ('{CLI_RESOURCES_PATH}', 'logs'),
168
+
154
169
  'PLUGINS_RESOURCES_PATH' : ('{INTERNAL_RESOURCES_PATH}', 'plugins'),
155
170
  'PLUGINS_INTERNAL_LOCK_PATH' : ('{INTERNAL_RESOURCES_PATH}', 'plugins.lock'),
156
171
  'PLUGINS_PACKAGES_INTERNAL_PATH' : ('{INTERNAL_RESOURCES_PATH}', 'packaged_plugins'),
157
172
  'PLUGINS_ARCHIVES_RESOURCES_PATH': ('{PLUGINS_RESOURCES_PATH}', '.archives'),
158
173
  'PLUGINS_TEMP_RESOURCES_PATH' : ('{PLUGINS_RESOURCES_PATH}', '.tmp'),
159
174
  'PLUGINS_INIT_PATH' : ('{PLUGINS_RESOURCES_PATH}', '__init__.py'),
175
+ 'PLUGINS_INJECTED_RESOURCES_PATH': ('{PLUGINS_RESOURCES_PATH}', '.injected'),
160
176
 
161
177
  'DB_RESOURCES_PATH' : ('{ROOT_DIR_PATH}', 'db'),
162
178
  'DB_INIT_RESOURCES_PATH' : ('{DB_RESOURCES_PATH}', 'initdb'),
@@ -197,11 +213,110 @@ paths = {
197
213
 
198
214
  def set_root(root: Union[Path, str]):
199
215
  """Modify the value of `ROOT_DIR_PATH`."""
200
- paths['ROOT_DIR_PATH'] = Path(root).resolve()
216
+ paths['ROOT_DIR_PATH'] = Path(root).resolve().as_posix()
201
217
  for path_name, path_parts in paths.items():
202
218
  if isinstance(path_parts, tuple) and path_parts[0] == '{ROOT_DIR_PATH}':
203
219
  globals()[path_name] = __getattr__(path_name)
204
220
 
221
+ def set_plugins_dir_paths(plugins_dir_paths: Union[List[Path], Path, str]):
222
+ """Modify the value of `PLUGINS_DIR_PATHS`."""
223
+ global PLUGINS_DIR_PATHS
224
+ if isinstance(plugins_dir_paths, str):
225
+ if plugins_dir_paths.strip() and plugins_dir_paths.lstrip()[0] == '[':
226
+ plugins_dir_paths = json.loads(plugins_dir_paths)
227
+ else:
228
+ plugins_dir_paths = plugins_dir_paths.split(':')
229
+ plugins_dir_paths = [Path(_path).resolve() for _path in plugins_dir_paths]
230
+ if isinstance(plugins_dir_paths, Path):
231
+ plugins_dir_paths = [plugins_dir_paths.resolve()]
232
+
233
+ PLUGINS_DIR_PATHS = plugins_dir_paths
234
+ _process_plugins_dir_paths(plugins_dir_paths)
235
+
236
+ def set_venvs_dir_path(venvs_dir_path: Union[str, Path]):
237
+ """Modify the value of `VIRTENV_RESOURCES_PATH`."""
238
+ paths['VIRTENV_RESOURCES_PATH'] = Path(venvs_dir_path).resolve().as_posix()
239
+
240
+ def set_config_dir_path(config_dir_path: Union[str, Path]):
241
+ paths['CONFIG_DIR_PATH'] = Path(config_dir_path).resolve().as_posix()
242
+
243
+
244
+ @contextlib.contextmanager
245
+ def replace_root_dir(root_dir_path: Union[str, Path, None]):
246
+ """
247
+ Temporarily replace the root directory path.
248
+ """
249
+ if root_dir_path is None:
250
+ try:
251
+ yield
252
+ finally:
253
+ return
254
+
255
+ old_root = paths.get('ROOT_DIR_PATH', _ROOT_DIR_PATH)
256
+ set_root(root_dir_path)
257
+
258
+ try:
259
+ yield
260
+ finally:
261
+ set_root(old_root)
262
+
263
+ @contextlib.contextmanager
264
+ def replace_plugins_dir_paths(plugins_dir_paths: Union[List[Path], None]):
265
+ """
266
+ Temporarily replace the plugins directory paths.
267
+ """
268
+ if plugins_dir_paths is None:
269
+ try:
270
+ yield
271
+ finally:
272
+ return
273
+
274
+ old_plugins_dir_paths = PLUGINS_DIR_PATHS
275
+ set_plugins_dir_paths(plugins_dir_paths)
276
+
277
+ try:
278
+ yield
279
+ finally:
280
+ set_plugins_dir_paths(old_plugins_dir_paths)
281
+
282
+ @contextlib.contextmanager
283
+ def replace_venvs_dir_path(venvs_dir_path: Union[Path, None]):
284
+ """
285
+ Temporarily replace the virtual environments directory path.
286
+ """
287
+ if venvs_dir_path is None:
288
+ try:
289
+ yield
290
+ finally:
291
+ return
292
+
293
+ old_venvs_dir_path = paths.get('VIRTENV_RESOURCES_PATH', _VENVS_DIR_PATH)
294
+ set_venvs_dir_path(venvs_dir_path)
295
+
296
+ try:
297
+ yield
298
+ finally:
299
+ set_venvs_dir_path(old_venvs_dir_path)
300
+
301
+ @contextlib.contextmanager
302
+ def replace_config_dir_path(config_dir_path: Union[Path, None]):
303
+ """
304
+ Temporarily replace the config directory path.
305
+ """
306
+ if config_dir_path is None:
307
+ try:
308
+ yield
309
+ finally:
310
+ return
311
+
312
+ old_config_dir_path = paths.get('CONFIG_DIR_PATH', _CONFIG_DIR_PATH)
313
+ set_config_dir_path(config_dir_path)
314
+
315
+ try:
316
+ yield
317
+ finally:
318
+ set_config_dir_path(old_config_dir_path)
319
+
205
320
 
206
321
  def __getattr__(name: str) -> Path:
207
322
  if name not in paths:
@@ -8,7 +8,9 @@ Import the config yaml file
8
8
  from __future__ import annotations
9
9
  import pathlib
10
10
 
11
+ import meerschaum as mrsm
11
12
  from meerschaum.utils.typing import Optional, Dict, Any, List, Tuple, Union
13
+ from meerschaum._internal.static import STATIC_CONFIG
12
14
 
13
15
 
14
16
  def read_config(
@@ -53,7 +55,6 @@ def read_config(
53
55
  import itertools
54
56
  from meerschaum.utils.yaml import yaml, _yaml
55
57
  from meerschaum.config._paths import CONFIG_DIR_PATH
56
- from meerschaum._internal.static import STATIC_CONFIG
57
58
  from meerschaum.config._patch import apply_patch_to_config
58
59
  if directory is None:
59
60
  directory = CONFIG_DIR_PATH
@@ -106,20 +107,25 @@ def read_config(
106
107
  ### If default config contains symlinks, add them to the config to write.
107
108
  try:
108
109
  _default_symlinks = _default_dict[symlinks_key][mk]
109
- except Exception:
110
+ except KeyError:
110
111
  _default_symlinks = {}
112
+
111
113
  config[mk] = _default_dict[mk]
112
114
  if _default_symlinks:
113
115
  if symlinks_key not in config:
114
116
  config[symlinks_key] = {}
117
+ if symlinks_key not in config_to_write:
118
+ config_to_write[symlinks_key] = {}
119
+
115
120
  if mk not in config[symlinks_key]:
116
121
  config[symlinks_key][mk] = {}
122
+ if mk not in config_to_write[symlinks_key]:
123
+ config_to_write[symlinks_key][mk] = {}
124
+
117
125
  config[symlinks_key][mk] = apply_patch_to_config(
118
126
  config[symlinks_key][mk],
119
127
  _default_symlinks
120
128
  )
121
- if symlinks_key not in config_to_write:
122
- config_to_write[symlinks_key] = {}
123
129
  config_to_write[symlinks_key][mk] = config[symlinks_key][mk]
124
130
 
125
131
  ### Write the default key.
@@ -387,10 +393,16 @@ def search_and_substitute_config(
387
393
  s[_keys[-1]] = _pattern
388
394
 
389
395
  from meerschaum.config._patch import apply_patch_to_config
390
- from meerschaum._internal.static import STATIC_CONFIG
391
396
  symlinks_key = STATIC_CONFIG['config']['symlinks_key']
392
- if symlinks_key in parsed_config:
393
- parsed_config[symlinks_key] = apply_patch_to_config(parsed_config[symlinks_key], symlinks)
397
+ if symlinks:
398
+ if symlinks_key not in parsed_config:
399
+ parsed_config[symlinks_key] = symlinks
400
+ else:
401
+ parsed_config[symlinks_key] = apply_patch_to_config(
402
+ parsed_config[symlinks_key],
403
+ symlinks,
404
+ warn=False,
405
+ )
394
406
 
395
407
  return parsed_config
396
408
 
@@ -410,13 +422,12 @@ def revert_symlinks_config(config: Dict[str, Any]) -> Dict[str, Any]:
410
422
  A configuration dictionary with `_symlinks` re-applied.
411
423
  """
412
424
  from meerschaum.config import apply_patch_to_config
413
- from meerschaum._internal.static import STATIC_CONFIG
414
425
  symlinks_key = STATIC_CONFIG['config']['symlinks_key']
415
426
  if symlinks_key not in config:
416
427
  return config
417
428
 
418
429
  symlinks_config = config[symlinks_key]
419
- reverted_config = apply_patch_to_config(config, symlinks_config)
430
+ reverted_config = apply_patch_to_config(symlinks_config, config, warn=False)
420
431
  _ = reverted_config.pop(symlinks_key, None)
421
432
  return reverted_config
422
433
 
@@ -462,7 +473,6 @@ def get_keyfile_path(
462
473
  )
463
474
  except IndexError:
464
475
  if create_new:
465
- from meerschaum._internal.static import STATIC_CONFIG
466
476
  default_filetype = STATIC_CONFIG['config']['default_filetype']
467
477
  return pathlib.Path(os.path.join(directory, key + '.' + default_filetype))
468
478
  return None
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "3.0.0rc4"
5
+ __version__ = "3.0.0rc7"