meerschaum 1.7.2__py3-none-any.whl → 1.7.3__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.
@@ -43,6 +43,7 @@ def stack(
43
43
  attempt_import, run_python_package, venv_contains_package,
44
44
  pip_install,
45
45
  )
46
+ from meerschaum.config._sync import sync_files
46
47
  from meerschaum.config import get_config
47
48
  from meerschaum.utils.debug import dprint
48
49
  from meerschaum.utils.warnings import warn
@@ -73,9 +74,10 @@ def stack(
73
74
  if not path.exists():
74
75
  bootstrap = True
75
76
  break
76
- ### if bootstrap flag was set, create files
77
77
  if bootstrap:
78
78
  write_stack(debug=debug)
79
+ else:
80
+ sync_files(['stack'])
79
81
 
80
82
  compose_command = ['up']
81
83
  ### default: alias stack as docker-compose
@@ -10,7 +10,7 @@ from __future__ import annotations
10
10
  from meerschaum.utils.venv import Venv
11
11
  from meerschaum.utils.packages import attempt_import, import_dcc, import_html
12
12
  from meerschaum.utils.typing import SuccessTuple, List
13
- from meerschaum.config.static import _static_config
13
+ from meerschaum.config.static import STATIC_CONFIG
14
14
  from meerschaum.utils.misc import remove_ansi
15
15
  from meerschaum.actions import get_shell
16
16
  from meerschaum.api import endpoints, CHECK_UPDATE
@@ -154,7 +154,7 @@ def alert_from_success_tuple(success: SuccessTuple) -> dbc.Alert:
154
154
  id = 'success-alert',
155
155
  dismissable = True,
156
156
  fade = True,
157
- is_open = not (success[1] in _static_config()['system']['success']['ignore']),
157
+ is_open = not (success[1] in STATIC_CONFIG['system']['success']['ignore']),
158
158
  color = 'success' if success[0] else 'danger',
159
159
  )
160
160
  )
@@ -45,12 +45,13 @@ def _config(
45
45
  if config is None or reload:
46
46
  with _locks['config']:
47
47
  config = {}
48
+
48
49
  if keys and keys[0] not in config:
49
50
  from meerschaum.config._sync import sync_files as _sync_files
50
51
  key_config = read_config(
51
- keys=[keys[0]],
52
- substitute=substitute,
53
- write_missing=write_missing,
52
+ keys = [keys[0]],
53
+ substitute = substitute,
54
+ write_missing = write_missing,
54
55
  )
55
56
  if keys[0] in key_config:
56
57
  config[keys[0]] = key_config[keys[0]]
@@ -232,7 +233,6 @@ def get_config(
232
233
  )
233
234
  )
234
235
  if patch and keys[0] != symlinks_key:
235
- # print("Updating configuration, please wait...")
236
236
  if write_missing:
237
237
  write_config(config, debug=debug)
238
238
 
@@ -71,7 +71,7 @@ def write_config(
71
71
  if directory is None:
72
72
  from meerschaum.config._paths import CONFIG_DIR_PATH
73
73
  directory = CONFIG_DIR_PATH
74
- from meerschaum.config.static import _static_config
74
+ from meerschaum.config.static import STATIC_CONFIG
75
75
  from meerschaum.config._default import default_header_comment
76
76
  from meerschaum.config._patch import apply_patch_to_config
77
77
  from meerschaum.config._read_config import get_keyfile_path
@@ -84,14 +84,14 @@ def write_config(
84
84
  cf = _config()
85
85
  config_dict = cf
86
86
 
87
- default_filetype = _static_config()['config']['default_filetype']
87
+ default_filetype = STATIC_CONFIG['config']['default_filetype']
88
88
  filetype_dumpers = {
89
89
  'yml' : yaml.dump,
90
90
  'yaml' : yaml.dump,
91
91
  'json' : json.dump,
92
92
  }
93
93
 
94
- symlinks_key = _static_config()['config']['symlinks_key']
94
+ symlinks_key = STATIC_CONFIG['config']['symlinks_key']
95
95
  symlinks = config_dict.pop(symlinks_key) if symlinks_key in config_dict else {}
96
96
  config_dict = apply_patch_to_config(config_dict, symlinks)
97
97
 
@@ -148,27 +148,17 @@ def write_config(
148
148
  return True
149
149
 
150
150
  def general_write_yaml_config(
151
- files : Optional[Dict[str, pathlib.Path]] = None,
152
- debug : bool = False
151
+ files: Optional[Dict[pathlib.Path, Dict[str, Any]]] = None,
152
+ debug: bool = False
153
153
  ):
154
- """Write configuration dictionaries to file paths with optional headers.
155
-
156
- files : dict
157
- Dictionary of paths -> dict or tuple of format (dict, header).
158
- If item is a tuple, the header will be written at the top of the file.
154
+ """
155
+ Write configuration dictionaries to file paths with optional headers.
159
156
 
160
157
  Parameters
161
158
  ----------
162
- files : Optional[Dict[str :
163
-
164
- pathlib.Path]] :
165
- (Default value = None)
166
- debug : bool :
167
- (Default value = False)
168
-
169
- Returns
170
- -------
171
-
159
+ files: Optional[Dict[str, pathlib.Path]], default None
160
+ Dictionary of paths -> dict or tuple of format (dict, header).
161
+ If item is a tuple, the header will be written at the top of the file.
172
162
  """
173
163
 
174
164
  from meerschaum.utils.debug import dprint
@@ -46,7 +46,7 @@ def read_config(
46
46
  from meerschaum.utils.packages import attempt_import
47
47
  from meerschaum.utils.yaml import yaml, _yaml
48
48
  from meerschaum.config._paths import CONFIG_DIR_PATH
49
- from meerschaum.config.static import _static_config
49
+ from meerschaum.config.static import STATIC_CONFIG
50
50
  from meerschaum.config._patch import apply_patch_to_config
51
51
  if directory is None:
52
52
  directory = CONFIG_DIR_PATH
@@ -57,11 +57,11 @@ def read_config(
57
57
  return default_config
58
58
 
59
59
  ### Each key corresponds to a YAML or JSON file.
60
- symlinks_key = _static_config()['config']['symlinks_key']
60
+ symlinks_key = STATIC_CONFIG['config']['symlinks_key']
61
61
  config = {}
62
62
  config_to_write = {}
63
63
 
64
- default_filetype = _static_config()['config']['default_filetype']
64
+ default_filetype = STATIC_CONFIG['config']['default_filetype']
65
65
  filetype_loaders = {
66
66
  'yml' : yaml.load,
67
67
  'yaml' : yaml.load,
@@ -187,7 +187,6 @@ def read_config(
187
187
  print(f"Unable to parse {filename}!")
188
188
  import traceback
189
189
  traceback.print_exc()
190
- # print(e)
191
190
  input(f"Press [Enter] to open '{filename}' and fix formatting errors.")
192
191
  from meerschaum.utils.misc import edit_file
193
192
  edit_file(filepath)
@@ -363,8 +362,8 @@ def search_and_substitute_config(
363
362
  s[_keys[-1]] = _pattern
364
363
 
365
364
  from meerschaum.config._patch import apply_patch_to_config
366
- from meerschaum.config.static import _static_config
367
- symlinks_key = _static_config()['config']['symlinks_key']
365
+ from meerschaum.config.static import STATIC_CONFIG
366
+ symlinks_key = STATIC_CONFIG['config']['symlinks_key']
368
367
  if symlinks_key not in parsed_config:
369
368
  parsed_config[symlinks_key] = {}
370
369
  parsed_config[symlinks_key] = apply_patch_to_config(parsed_config[symlinks_key], symlinks)
@@ -403,16 +402,16 @@ def get_keyfile_path(
403
402
  os.path.join(
404
403
  directory,
405
404
  read_config(
406
- keys=[key],
407
- with_filenames=True,
408
- write_missing=False,
409
- substitute=False,
405
+ keys = [key],
406
+ with_filenames = True,
407
+ write_missing = False,
408
+ substitute = False,
410
409
  )[1][0]
411
410
  )
412
411
  )
413
412
  except IndexError as e:
414
413
  if create_new:
415
- from meerschaum.config.static import _static_config
416
- default_filetype = _static_config()['config']['default_filetype']
414
+ from meerschaum.config.static import STATIC_CONFIG
415
+ default_filetype = STATIC_CONFIG['config']['default_filetype']
417
416
  return pathlib.Path(os.path.join(directory, key + '.' + default_filetype))
418
417
  return None
@@ -10,7 +10,6 @@ from __future__ import annotations
10
10
  from meerschaum.utils.typing import Optional, List, Tuple
11
11
 
12
12
  def sync_yaml_configs(
13
- config_path: pathlib.Path,
14
13
  keys: List[str],
15
14
  sub_path: pathlib.Path,
16
15
  substitute: bool = True,
@@ -19,28 +18,24 @@ def sync_yaml_configs(
19
18
  ) -> None:
20
19
  """Synchronize sub-configuration with main configuration file.
21
20
 
22
- NOTE: This function might need refactoring to work better with the new
23
- `read_config` system.
24
-
25
21
  Parameters
26
22
  ----------
27
- config_path :
28
- Not sure if this is necessary.
29
- keys :
23
+ keys: List[str]
30
24
  The config keys to read via `get_config()`.
31
- sub_path :
25
+
26
+ sub_path: pathlib.Path
32
27
  The derivative file to write.
33
- substitute :
28
+
29
+ substitute: bool, default True
34
30
  If `True`, parse `MRSM{}` syntax and substitute values.
35
31
  See `get_config()` for more information.
36
- Defaults to `True`.
37
- permissions :
32
+
33
+ permissions: Optional[int], default None
38
34
  If not `None`, set permissions of the derivative file.
39
- Defaults to `None`.
40
- replace_tuples :
35
+
36
+ replace_tuples: Optional[List[Tuple[str]]], default None
41
37
  If provided, iterate through a list of tuples,
42
38
  replacing the old string (index 0) with the new string (index 1).
43
- Defaults to `None`.
44
39
  """
45
40
  import os, sys
46
41
  try:
@@ -50,12 +45,12 @@ def sync_yaml_configs(
50
45
  from meerschaum.config._patch import apply_patch_to_config
51
46
  import meerschaum.config
52
47
  from meerschaum.utils.packages import reload_package
53
- from meerschaum.config._read_config import search_and_substitute_config
54
- if not os.path.isfile(config_path) or not os.path.isfile(sub_path):
55
- return
48
+ from meerschaum.config._read_config import search_and_substitute_config, get_keyfile_path
56
49
 
57
- def _read_config(path):
50
+ def _read_yaml_config(path):
58
51
  """Read YAML file with header comment."""
52
+ if not path.exists():
53
+ return "", {}
59
54
  header_comment = ""
60
55
  with open(path, 'r') as f:
61
56
  if _yaml is not None:
@@ -70,17 +65,16 @@ def sync_yaml_configs(
70
65
  header_comment += line
71
66
  return header_comment, config
72
67
 
73
- config_header, config = _read_config(config_path)
74
- sub_header, sub_config = _read_config(sub_path)
75
-
76
68
  from meerschaum.config import get_config
69
+ config_path = get_keyfile_path(keys[0], create_new=True)
77
70
  c = get_config(*keys, substitute=substitute, sync_files=False)
71
+
72
+ config_header, config = _read_yaml_config(config_path)
73
+ sub_header, sub_config = _read_yaml_config(sub_path)
74
+
78
75
  if substitute:
79
76
  sub_config = search_and_substitute_config(sub_config)
80
77
 
81
- config_time = os.path.getmtime(config_path)
82
- sub_time = os.path.getmtime(sub_path)
83
-
84
78
  sub_config = c
85
79
  new_config_text = yaml.dump(c, sort_keys=False)
86
80
  if replace_tuples:
@@ -99,28 +93,32 @@ def sync_yaml_configs(
99
93
  def sync_files(keys: Optional[List[str]] = None):
100
94
  if keys is None:
101
95
  keys = []
96
+
102
97
  def _stack():
103
98
  import os
104
- from meerschaum.config._paths import CONFIG_DIR_PATH, STACK_ENV_PATH, STACK_COMPOSE_PATH
105
- from meerschaum.config._paths import STACK_COMPOSE_FILENAME, STACK_ENV_FILENAME
106
- from meerschaum.config._paths import GRAFANA_DATASOURCE_PATH, GRAFANA_DASHBOARD_PATH
107
- from meerschaum.config._paths import MOSQUITTO_CONFIG_PATH
99
+ from meerschaum.config._paths import (
100
+ CONFIG_DIR_PATH,
101
+ STACK_ENV_PATH,
102
+ STACK_COMPOSE_PATH,
103
+ STACK_COMPOSE_FILENAME,
104
+ STACK_ENV_FILENAME,
105
+ GRAFANA_DATASOURCE_PATH,
106
+ GRAFANA_DASHBOARD_PATH,
107
+ )
108
+ from meerschaum.config.static import STATIC_CONFIG
108
109
 
109
110
  sync_yaml_configs(
110
- CONFIG_DIR_PATH / 'stack.yaml',
111
111
  ['stack', STACK_COMPOSE_FILENAME],
112
112
  STACK_COMPOSE_PATH,
113
113
  substitute = True,
114
114
  replace_tuples = [('$', '$$'), ('<DOLLAR>', '$')],
115
115
  )
116
116
  sync_yaml_configs(
117
- CONFIG_DIR_PATH / 'stack.yaml',
118
117
  ['stack', 'grafana', 'datasource'],
119
118
  GRAFANA_DATASOURCE_PATH,
120
119
  substitute = True,
121
120
  )
122
121
  sync_yaml_configs(
123
- CONFIG_DIR_PATH / 'stack.yaml',
124
122
  ['stack', 'grafana', 'dashboard'],
125
123
  GRAFANA_DASHBOARD_PATH,
126
124
  substitute = True,
@@ -128,7 +126,7 @@ def sync_files(keys: Optional[List[str]] = None):
128
126
 
129
127
 
130
128
  key_functions = {
131
- 'stack' : _stack,
129
+ 'stack': _stack,
132
130
  }
133
131
  if keys is None:
134
132
  keys = list(key_functions.keys())
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "1.7.2"
5
+ __version__ = "1.7.3"
@@ -8,12 +8,13 @@ Insert non-user-editable configuration files here.
8
8
 
9
9
  import os
10
10
  import uuid
11
+ from typing import Dict, Any
11
12
  from meerschaum.utils.misc import generate_password
12
13
 
13
14
  __all__ = ['STATIC_CONFIG']
14
15
 
15
- SERVER_ID = os.environ.get('MRSM_SERVER_ID', generate_password(6))
16
- STATIC_CONFIG = {
16
+ SERVER_ID: str = os.environ.get('MRSM_SERVER_ID', generate_password(6))
17
+ STATIC_CONFIG: Dict[str, Any] = {
17
18
  'api': {
18
19
  'endpoints': {
19
20
  'index': '/',
@@ -84,6 +85,9 @@ STATIC_CONFIG = {
84
85
  'connectors': {
85
86
  'default_label': 'main',
86
87
  },
88
+ 'stack': {
89
+ 'dollar_standin': '<DOLLAR>', ### Temporary replacement for docker-compose.yaml.
90
+ },
87
91
  'users': {
88
92
  'password_hash': {
89
93
  'schemes': [
@@ -50,7 +50,7 @@ def do_action(
50
50
  """
51
51
  import sys, json
52
52
  from meerschaum.utils.debug import dprint
53
- from meerschaum.config.static import _static_config
53
+ from meerschaum.config.static import STATIC_CONFIG
54
54
  from meerschaum.utils.misc import json_serialize_datetime
55
55
  if action is None:
56
56
  action = []
@@ -67,7 +67,7 @@ def do_action(
67
67
 
68
68
  root_action = json_dict['action'][0]
69
69
  del json_dict['action'][0]
70
- r_url = f"{_static_config()['api']['endpoints']['actions']}/{root_action}"
70
+ r_url = f"{STATIC_CONFIG['api']['endpoints']['actions']}/{root_action}"
71
71
 
72
72
  if debug:
73
73
  from meerschaum.utils.formatting import pprint
@@ -18,7 +18,7 @@ def login(
18
18
  """Log in and set the session token."""
19
19
  from meerschaum.utils.warnings import warn as _warn, info, error
20
20
  from meerschaum.core import User
21
- from meerschaum.config.static import _static_config
21
+ from meerschaum.config.static import STATIC_CONFIG
22
22
  import json, datetime
23
23
  try:
24
24
  login_data = {
@@ -28,7 +28,7 @@ def login(
28
28
  except AttributeError:
29
29
  return False, f"Please login with the command `login {self}`."
30
30
  response = self.post(
31
- _static_config()['api']['endpoints']['login'],
31
+ STATIC_CONFIG['api']['endpoints']['login'],
32
32
  data = login_data,
33
33
  use_token = False,
34
34
  debug = debug
@@ -11,22 +11,12 @@ from meerschaum.utils.typing import Optional
11
11
 
12
12
  def get_mrsm_version(self, **kw) -> Optional[str]:
13
13
  """
14
-
15
- Parameters
16
- ----------
17
- **kw :
18
-
19
-
20
- Returns
21
- -------
22
- type
23
-
24
-
14
+ Return the Meerschaum version of the API instance.
25
15
  """
26
- from meerschaum.config.static import _static_config
16
+ from meerschaum.config.static import STATIC_CONFIG
27
17
  try:
28
18
  j = self.get(
29
- _static_config()['api']['endpoints']['version'] + '/mrsm',
19
+ STATIC_CONFIG['api']['endpoints']['version'] + '/mrsm',
30
20
  use_token = True,
31
21
  **kw
32
22
  ).json()
@@ -38,22 +28,12 @@ def get_mrsm_version(self, **kw) -> Optional[str]:
38
28
 
39
29
  def get_chaining_status(self, **kw) -> Optional[bool]:
40
30
  """
41
-
42
- Parameters
43
- ----------
44
- **kw :
45
-
46
-
47
- Returns
48
- -------
49
- type
50
-
51
-
31
+ Fetch the chaining status of the API instance.
52
32
  """
53
- from meerschaum.config.static import _static_config
33
+ from meerschaum.config.static import STATIC_CONFIG
54
34
  try:
55
35
  response = self.get(
56
- _static_config()['api']['endpoints']['chaining'],
36
+ STATIC_CONFIG['api']['endpoints']['chaining'],
57
37
  use_token = True,
58
38
  **kw
59
39
  )
@@ -13,8 +13,8 @@ def plugin_r_url(
13
13
  plugin: Union[meerschaum.core.Plugin, str]
14
14
  ) -> str:
15
15
  """Generate a relative URL path from a Plugin."""
16
- from meerschaum.config.static import _static_config
17
- return f"{_static_config()['api']['endpoints']['plugins']}/{plugin}"
16
+ from meerschaum.config.static import STATIC_CONFIG
17
+ return f"{STATIC_CONFIG['api']['endpoints']['plugins']}/{plugin}"
18
18
 
19
19
  def register_plugin(
20
20
  self,
@@ -105,9 +105,9 @@ def get_plugins(
105
105
  """
106
106
  import json
107
107
  from meerschaum.utils.warnings import warn, error
108
- from meerschaum.config.static import _static_config
108
+ from meerschaum.config.static import STATIC_CONFIG
109
109
  response = self.get(
110
- _static_config()['api']['endpoints']['plugins'],
110
+ STATIC_CONFIG['api']['endpoints']['plugins'],
111
111
  params = {'user_id' : user_id, 'search_term' : search_term},
112
112
  use_token = True,
113
113
  debug = debug
@@ -129,7 +129,6 @@ def get_plugin_attributes(
129
129
  """
130
130
  import json
131
131
  from meerschaum.utils.warnings import warn, error
132
- from meerschaum.config.static import _static_config
133
132
  r_url = plugin_r_url(plugin) + '/attributes'
134
133
  response = self.get(r_url, use_token=True, debug=debug)
135
134
  attributes = response.json()
@@ -17,10 +17,10 @@ def get_users(
17
17
  """
18
18
  Return a list of registered usernames.
19
19
  """
20
- from meerschaum.config.static import _static_config
20
+ from meerschaum.config.static import STATIC_CONFIG
21
21
  import json
22
22
  response = self.get(
23
- f"{_static_config()['api']['endpoints']['users']}",
23
+ f"{STATIC_CONFIG['api']['endpoints']['users']}",
24
24
  debug = debug,
25
25
  use_token = True,
26
26
  )
@@ -69,8 +69,8 @@ def register_user(
69
69
  ) -> SuccessTuple:
70
70
  """Register a new user."""
71
71
  import json
72
- from meerschaum.config.static import _static_config
73
- r_url = f"{_static_config()['api']['endpoints']['users']}/register"
72
+ from meerschaum.config.static import STATIC_CONFIG
73
+ r_url = f"{STATIC_CONFIG['api']['endpoints']['users']}/register"
74
74
  data = {
75
75
  'username': user.username,
76
76
  'password': user.password,
@@ -100,9 +100,9 @@ def get_user_id(
100
100
  **kw: Any
101
101
  ) -> Optional[int]:
102
102
  """Get a user's ID."""
103
- from meerschaum.config.static import _static_config
103
+ from meerschaum.config.static import STATIC_CONFIG
104
104
  import json
105
- r_url = f"{_static_config()['api']['endpoints']['users']}/{user.username}/id"
105
+ r_url = f"{STATIC_CONFIG['api']['endpoints']['users']}/{user.username}/id"
106
106
  response = self.get(r_url, debug=debug, **kw)
107
107
  try:
108
108
  user_id = int(json.loads(response.text))
@@ -117,9 +117,9 @@ def delete_user(
117
117
  **kw: Any
118
118
  ) -> SuccessTuple:
119
119
  """Delete a user."""
120
- from meerschaum.config.static import _static_config
120
+ from meerschaum.config.static import STATIC_CONFIG
121
121
  import json
122
- r_url = f"{_static_config()['api']['endpoints']['users']}/{user.username}"
122
+ r_url = f"{STATIC_CONFIG['api']['endpoints']['users']}/{user.username}"
123
123
  response = self.delete(r_url, debug=debug)
124
124
  try:
125
125
  _json = json.loads(response.text)
@@ -137,9 +137,9 @@ def get_user_attributes(
137
137
  **kw
138
138
  ) -> int:
139
139
  """Get a user's attributes."""
140
- from meerschaum.config.static import _static_config
140
+ from meerschaum.config.static import STATIC_CONFIG
141
141
  import json
142
- r_url = f"{_static_config()['api']['endpoints']['users']}/{user.username}/attributes"
142
+ r_url = f"{STATIC_CONFIG['api']['endpoints']['users']}/{user.username}/attributes"
143
143
  response = self.get(r_url, debug=debug, **kw)
144
144
  try:
145
145
  attributes = json.loads(response.text)
@@ -158,8 +158,8 @@ def get_user_password_hash(
158
158
  **kw: Any
159
159
  ) -> Optional[str]:
160
160
  """If configured, get a user's password hash."""
161
- from meerschaum.config.static import _static_config
162
- r_url = _static_config()['api']['endpoints']['users'] + '/' + user.username + '/password_hash'
161
+ from meerschaum.config.static import STATIC_CONFIG
162
+ r_url = STATIC_CONFIG['api']['endpoints']['users'] + '/' + user.username + '/password_hash'
163
163
  response = self.get(r_url, debug=debug, **kw)
164
164
  if not response:
165
165
  return None
@@ -172,8 +172,8 @@ def get_user_type(
172
172
  **kw: Any
173
173
  ) -> Optional[str]:
174
174
  """If configured, get a user's type."""
175
- from meerschaum.config.static import _static_config
176
- r_url = _static_config()['api']['endpoints']['users'] + '/' + user.username + '/type'
175
+ from meerschaum.config.static import STATIC_CONFIG
176
+ r_url = STATIC_CONFIG['api']['endpoints']['users'] + '/' + user.username + '/type'
177
177
  response = self.get(r_url, debug=debug, **kw)
178
178
  if not response:
179
179
  return None
@@ -53,14 +53,14 @@ def parse_connector_keys(
53
53
  import copy
54
54
  from meerschaum.connectors import get_connector
55
55
  from meerschaum.config import get_config
56
- from meerschaum.config.static import _static_config
56
+ from meerschaum.config.static import STATIC_CONFIG
57
57
  from meerschaum.utils.warnings import error
58
58
 
59
59
  ### `get_connector()` handles the logic for falling back to 'main',
60
60
  ### so don't make any decisions here.
61
61
  vals = str(keys).split(':')
62
62
  _type = vals[0]
63
- _label = vals[1] if len(vals) > 1 else _static_config()['connectors']['default_label']
63
+ _label = vals[1] if len(vals) > 1 else STATIC_CONFIG['connectors']['default_label']
64
64
  _get_connector_kw = {'type': _type, 'label': _label}
65
65
  _get_connector_kw.update(kw)
66
66
 
@@ -70,7 +70,7 @@ def valid_username(username: str) -> SuccessTuple:
70
70
  if len(username) > max_length:
71
71
  fail_reasons.append(f"Usernames must contain {max_length} or fewer characters.")
72
72
 
73
- acceptable_chars = {'_', '-'}
73
+ acceptable_chars = {'_', '-', '.', '@'}
74
74
  for c in username:
75
75
  if not c.isalnum() and c not in acceptable_chars:
76
76
  fail_reasons.append(
@@ -13,9 +13,9 @@ pwd_context = None
13
13
  def get_pwd_context():
14
14
  global pwd_context
15
15
  if pwd_context is None:
16
- from meerschaum.config.static import _static_config
16
+ from meerschaum.config.static import STATIC_CONFIG
17
17
  from meerschaum.utils.packages import attempt_import
18
- hash_config = _static_config()['users']['password_hash']
18
+ hash_config = STATIC_CONFIG['users']['password_hash']
19
19
  passlib_context = attempt_import('passlib.context')
20
20
  pwd_context = passlib_context.CryptContext(
21
21
  schemes = hash_config['schemes'],
@@ -24,7 +24,11 @@ def get_pwd_context():
24
24
  )
25
25
  return pwd_context
26
26
 
27
- class User():
27
+ class User:
28
+ """
29
+ The Meerschaum User object manages authentication to a given instance.
30
+ """
31
+
28
32
  def __init__(
29
33
  self,
30
34
  username: str,
@@ -93,6 +93,8 @@ class Venv:
93
93
  Return the top-level path for this virtual environment.
94
94
  """
95
95
  from meerschaum.config._paths import VIRTENV_RESOURCES_PATH
96
+ if self._venv is None:
97
+ return self.target_path.parent
96
98
  return VIRTENV_RESOURCES_PATH / self._venv
97
99
 
98
100
 
@@ -579,16 +579,21 @@ def venv_target_path(
579
579
 
580
580
  if not inside_venv():
581
581
  site_path = pathlib.Path(site.getusersitepackages())
582
- ### Allow for dist-level paths (running as root).
583
582
  if not site_path.exists():
584
- if platform.system() == 'Windows' or os.geteuid() == 0:
585
- for possible_dist in reversed(site.getsitepackages()):
586
- dist_path = pathlib.Path(possible_dist)
587
- if not dist_path.exists():
588
- continue
589
- return dist_path
590
-
591
- raise EnvironmentError("Could not determine the dist-packages directory.")
583
+
584
+ ### Windows does not have `os.geteuid()`.
585
+ if platform.system() == 'Windows' or os.geteuid() != 0:
586
+ site_path.mkdir(parents=True, exist_ok=True)
587
+ return site_path
588
+
589
+ ### Allow for dist-level paths (running as root).
590
+ for possible_dist in reversed(site.getsitepackages()):
591
+ dist_path = pathlib.Path(possible_dist)
592
+ if not dist_path.exists():
593
+ continue
594
+ return dist_path
595
+
596
+ raise EnvironmentError("Could not determine the dist-packages directory.")
592
597
 
593
598
  return site_path
594
599