meerschaum 2.6.11__py3-none-any.whl → 2.6.13__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.
meerschaum/actions/api.py CHANGED
@@ -9,6 +9,7 @@ from __future__ import annotations
9
9
  import os
10
10
  from meerschaum.utils.typing import SuccessTuple, Optional, List, Any
11
11
 
12
+
12
13
  def api(
13
14
  action: Optional[List[str]] = None,
14
15
  sysargs: Optional[List[str]] = None,
@@ -36,7 +37,6 @@ def api(
36
37
 
37
38
  """
38
39
  from meerschaum.utils.warnings import warn, info
39
- from meerschaum.utils.formatting import print_tuple
40
40
  from meerschaum._internal.arguments._parse_arguments import parse_dict_to_sysargs
41
41
 
42
42
  if action is None:
@@ -88,6 +88,7 @@ def api(
88
88
  success, message = api_conn.do_action(sysargs)
89
89
  return success, message
90
90
 
91
+
91
92
  def _api_start(
92
93
  action: Optional[List[str]] = None,
93
94
  host: Optional[str] = None,
@@ -191,7 +192,7 @@ def _api_start(
191
192
 
192
193
  if keyfile or certfile:
193
194
  if not keyfile or not certfile:
194
- return False, f"HTTPS requires both `--keyfile` and `--certfile`."
195
+ return False, "HTTPS requires both `--keyfile` and `--certfile`."
195
196
 
196
197
  pool = get_pool(workers=workers)
197
198
  if pool is None:
@@ -33,6 +33,7 @@ import warnings
33
33
  ### Suppress the depreciation warnings from importing enrich.
34
34
  with warnings.catch_warnings():
35
35
  warnings.simplefilter("ignore")
36
+ _ = attempt_import('dataclass_wizard', lazy=False)
36
37
  enrich = attempt_import('dash_extensions.enrich', lazy=False)
37
38
  html, dcc = import_html(), import_dcc()
38
39
  from meerschaum.api.dash.components import location
@@ -978,9 +978,9 @@ def sign_out_button_click(
978
978
  Input({'type': 'parameters-as-json-button', 'index': MATCH}, 'n_clicks'),
979
979
  )
980
980
  def parameters_as_yaml_or_json_click(
981
- yaml_n_clicks: Optional[int],
982
- json_n_clicks: Optional[int],
983
- ):
981
+ yaml_n_clicks: Optional[int],
982
+ json_n_clicks: Optional[int],
983
+ ):
984
984
  """
985
985
  When the `YAML` button is clicked under the parameters editor, switch the content to YAML.
986
986
  """
@@ -18,6 +18,7 @@ from meerschaum.utils.misc import string_to_dict
18
18
  from meerschaum.utils.packages import attempt_import, import_dcc, import_html, import_pandas
19
19
  from meerschaum.utils.sql import get_pd_type
20
20
  from meerschaum.utils.yaml import yaml
21
+ from meerschaum.utils.warnings import warn
21
22
  from meerschaum.utils.dataframe import to_json
22
23
  from meerschaum.connectors.sql._fetch import get_pipe_query
23
24
  from meerschaum.api import CHECK_UPDATE
@@ -49,7 +50,7 @@ def pipe_from_ctx(ctx, trigger_property: str = 'n_clicks') -> Union[mrsm.Pipe, N
49
50
  ### Because Dash JSON-ifies the ID dictionary and we are including a JSON-ified dictionary,
50
51
  ### we have to do some crazy parsing to get the pipe's meta-dict back out of it
51
52
  meta = json.loads(json.loads(ctx[0]['prop_id'].split('.' + trigger_property)[0])['index'])
52
- except Exception as e:
53
+ except Exception:
53
54
  meta = None
54
55
  if meta is None:
55
56
  return None
@@ -115,6 +116,7 @@ def build_pipe_card(
115
116
  pipe: mrsm.Pipe,
116
117
  authenticated: bool = False,
117
118
  include_manage: bool = True,
119
+ _build_parents_num: int = 10,
118
120
  _build_children_num: int = 10,
119
121
  ) -> 'dbc.Card':
120
122
  """
@@ -220,6 +222,7 @@ def build_pipe_card(
220
222
  accordion_items_from_pipe(
221
223
  pipe,
222
224
  authenticated=authenticated,
225
+ _build_parents_num=_build_parents_num,
223
226
  _build_children_num=_build_children_num,
224
227
  ),
225
228
  flush=True,
@@ -235,7 +238,7 @@ def build_pipe_card(
235
238
  if pipe.instance_keys != default_instance:
236
239
  query_params['instance'] = pipe.instance_keys
237
240
  pipe_url = (
238
- f"/dash/pipes/"
241
+ "/dash/pipes/"
239
242
  + f"{pipe.connector_keys}/"
240
243
  + f"{pipe.metric_key}/"
241
244
  + (f"{pipe.location_key}" if pipe.location_key is not None else '')
@@ -322,6 +325,7 @@ def accordion_items_from_pipe(
322
325
  pipe: mrsm.Pipe,
323
326
  active_items: Optional[List[str]] = None,
324
327
  authenticated: bool = False,
328
+ _build_parents_num: int = 10,
325
329
  _build_children_num: int = 10,
326
330
  ) -> 'List[dbc.AccordionItem]':
327
331
  """
@@ -494,7 +498,7 @@ def accordion_items_from_pipe(
494
498
  else None
495
499
  )
496
500
  rowcount = pipe.get_rowcount(debug=debug)
497
- except Exception as e:
501
+ except Exception:
498
502
  oldest_time = None
499
503
  newest_time = None
500
504
  interval = None
@@ -532,44 +536,44 @@ def accordion_items_from_pipe(
532
536
  ]
533
537
  columns_body = [html.Tbody(columns_rows)]
534
538
  columns_table = dbc.Table(columns_header + columns_body, bordered=False, hover=True)
535
- except Exception as e:
539
+ except Exception:
536
540
  columns_table = html.P("Could not retrieve columns ― please try again.")
537
541
  items_bodies['columns'] = columns_table
538
542
 
539
543
  if 'parameters' in active_items:
540
544
  parameters_editor = dash_ace.DashAceEditor(
541
- value = yaml.dump(pipe.parameters),
542
- mode = 'norm',
543
- tabSize = 4,
544
- theme = 'twilight',
545
- id = {'type': 'parameters-editor', 'index': json.dumps(pipe.meta)},
546
- width = '100%',
547
- height = '500px',
548
- readOnly = False,
549
- showGutter = True,
550
- showPrintMargin = True,
551
- highlightActiveLine = True,
552
- wrapEnabled = True,
553
- style = {'min-height': '120px'},
545
+ value=yaml.dump(pipe.parameters),
546
+ mode='norm',
547
+ tabSize=4,
548
+ theme='twilight',
549
+ id={'type': 'parameters-editor', 'index': json.dumps(pipe.meta)},
550
+ width='100%',
551
+ height='500px',
552
+ readOnly=False,
553
+ showGutter=True,
554
+ showPrintMargin=True,
555
+ highlightActiveLine=True,
556
+ wrapEnabled=True,
557
+ style={'min-height': '120px'},
554
558
  )
555
559
  update_parameters_button = dbc.Button(
556
560
  "Update",
557
- id = {'type': 'update-parameters-button', 'index': json.dumps(pipe.meta)},
561
+ id={'type': 'update-parameters-button', 'index': json.dumps(pipe.meta)},
558
562
  )
559
563
 
560
564
  as_yaml_button = dbc.Button(
561
565
  "YAML",
562
- id = {'type': 'parameters-as-yaml-button', 'index': json.dumps(pipe.meta)},
563
- color = 'link',
564
- size = 'sm',
565
- style = {'text-decoration': 'none'},
566
+ id={'type': 'parameters-as-yaml-button', 'index': json.dumps(pipe.meta)},
567
+ color='link',
568
+ size='sm',
569
+ style={'text-decoration': 'none'},
566
570
  )
567
571
  as_json_button = dbc.Button(
568
572
  "JSON",
569
- id = {'type': 'parameters-as-json-button', 'index': json.dumps(pipe.meta)},
570
- color = 'link',
571
- size = 'sm',
572
- style = {'text-decoration': 'none', 'margin-left': '10px'},
573
+ id={'type': 'parameters-as-json-button', 'index': json.dumps(pipe.meta)},
574
+ color='link',
575
+ size='sm',
576
+ style={'text-decoration': 'none', 'margin-left': '10px'},
573
577
  )
574
578
  parameters_div_children = [
575
579
  parameters_editor,
@@ -593,6 +597,20 @@ def accordion_items_from_pipe(
593
597
  )
594
598
  ]),
595
599
  ]
600
+
601
+ if _build_parents_num > 0 and pipe.parents:
602
+ parents_cards = [
603
+ build_pipe_card(
604
+ parent_pipe,
605
+ authenticated = authenticated,
606
+ _build_parents_num = (_build_parents_num - 1),
607
+ )
608
+ for parent_pipe in pipe.parents
609
+ ]
610
+ parents_grid = build_cards_grid(parents_cards, num_columns=1)
611
+ parents_div_items = [html.Br(), html.H3('Parent Pipes'), html.Br(), parents_grid]
612
+ parameters_div_children.extend([html.Br()] + parents_div_items)
613
+
596
614
  if _build_children_num > 0 and pipe.children:
597
615
  children_cards = [
598
616
  build_pipe_card(
@@ -603,31 +621,31 @@ def accordion_items_from_pipe(
603
621
  for child_pipe in pipe.children
604
622
  ]
605
623
  children_grid = build_cards_grid(children_cards, num_columns=1)
606
- chidren_div_items = [html.Br(), html.H3('Children Pipes'), html.Br(), children_grid]
607
- parameters_div_children.extend([html.Br()] + chidren_div_items)
624
+ children_div_items = [html.Br(), html.H3('Children Pipes'), html.Br(), children_grid]
625
+ parameters_div_children.extend([html.Br()] + children_div_items)
608
626
 
609
627
  items_bodies['parameters'] = html.Div(parameters_div_children)
610
628
 
611
629
  if 'sql' in active_items:
612
630
  query = dedent((get_pipe_query(pipe, warn=False) or "")).lstrip().rstrip()
613
631
  sql_editor = dash_ace.DashAceEditor(
614
- value = query,
615
- mode = 'sql',
616
- tabSize = 4,
617
- theme = 'twilight',
618
- id = {'type': 'sql-editor', 'index': json.dumps(pipe.meta)},
619
- width = '100%',
620
- height = '500px',
621
- readOnly = False,
622
- showGutter = True,
623
- showPrintMargin = False,
624
- highlightActiveLine = True,
625
- wrapEnabled = True,
626
- style = {'min-height': '120px'},
632
+ value=query,
633
+ mode='sql',
634
+ tabSize=4,
635
+ theme='twilight',
636
+ id={'type': 'sql-editor', 'index': json.dumps(pipe.meta)},
637
+ width='100%',
638
+ height='500px',
639
+ readOnly=False,
640
+ showGutter=True,
641
+ showPrintMargin=False,
642
+ highlightActiveLine=True,
643
+ wrapEnabled=True,
644
+ style={'min-height': '120px'},
627
645
  )
628
646
  update_sql_button = dbc.Button(
629
647
  "Update",
630
- id = {'type': 'update-sql-button', 'index': json.dumps(pipe.meta)},
648
+ id={'type': 'update-sql-button', 'index': json.dumps(pipe.meta)},
631
649
  )
632
650
  items_bodies['sql'] = html.Div([
633
651
  sql_editor,
@@ -648,7 +666,7 @@ def accordion_items_from_pipe(
648
666
  try:
649
667
  df = pipe.get_backtrack_data(backtrack_minutes=10, limit=10, debug=debug).astype(str)
650
668
  table = dbc.Table.from_dataframe(df, bordered=False, hover=True)
651
- except Exception as e:
669
+ except Exception:
652
670
  table = html.P("Could not retrieve recent data.")
653
671
  items_bodies['recent-data'] = table
654
672
 
@@ -10,35 +10,20 @@ from __future__ import annotations
10
10
 
11
11
  from meerschaum.utils.typing import SuccessTuple, List, Dict, Any
12
12
  from meerschaum.api import (
13
- fastapi, app, endpoints, get_api_connector, debug, manager, private, no_auth
13
+ fastapi,
14
+ app,
15
+ endpoints,
16
+ get_api_connector,
17
+ debug,
18
+ manager,
19
+ private,
20
+ no_auth,
14
21
  )
15
22
  from meerschaum.actions import actions
16
- from meerschaum.config import get_config
23
+ from meerschaum.core.User import is_user_allowed_to_execute
17
24
 
18
25
  actions_endpoint = endpoints['actions']
19
26
 
20
- def is_user_allowed_to_execute(user) -> SuccessTuple:
21
- if user is None:
22
- return False, "Could not load user."
23
-
24
- if user.type == 'admin':
25
- return True, "Success"
26
-
27
- allow_non_admin = get_config(
28
- 'system', 'api', 'permissions', 'actions', 'non_admin', patch=True
29
- )
30
- if not allow_non_admin:
31
- return False, (
32
- "The administrator for this server has not allowed users to perform actions.\n\n"
33
- + "Please contact the system administrator, or if you are running this server, "
34
- + "open the configuration file with `edit config system` "
35
- + "and search for 'permissions'. "
36
- + "\nUnder the keys 'api:permissions:actions', "
37
- + "you can allow non-admin users to perform actions."
38
- )
39
-
40
- return True, "Success"
41
-
42
27
 
43
28
  @app.get(actions_endpoint, tags=['Actions'])
44
29
  def get_actions(
@@ -75,19 +60,9 @@ def do_action_legacy(
75
60
  -------
76
61
  A `SuccessTuple`.
77
62
  """
78
- if curr_user is not None and curr_user.type != 'admin':
79
- allow_non_admin = get_config(
80
- 'system', 'api', 'permissions', 'actions', 'non_admin', patch=True
81
- )
82
- if not allow_non_admin:
83
- return False, (
84
- "The administrator for this server has not allowed users to perform actions.\n\n"
85
- + "Please contact the system administrator, or if you are running this server, "
86
- + "open the configuration file with `edit config system` "
87
- + "and search for 'permissions'. "
88
- + "\nUnder the keys 'api:permissions:actions', "
89
- + "you can allow non-admin users to perform actions."
90
- )
63
+ allowed_success, allowed_msg = is_user_allowed_to_execute(curr_user, debug=debug)
64
+ if not allowed_success:
65
+ return allowed_success, allowed_msg
91
66
 
92
67
  if action not in actions:
93
68
  return False, f"Invalid action '{action}'."
@@ -30,8 +30,11 @@ from meerschaum.api import (
30
30
  endpoints,
31
31
  manager,
32
32
  no_auth,
33
+ debug,
33
34
  )
34
35
  from meerschaum.config.static import STATIC_CONFIG
36
+ from meerschaum.core.User import is_user_allowed_to_execute
37
+
35
38
 
36
39
  JOBS_STDIN_MESSAGE: str = STATIC_CONFIG['api']['jobs']['stdin_message']
37
40
  JOBS_STOP_MESSAGE: str = STATIC_CONFIG['api']['jobs']['stop_message']
@@ -141,6 +144,10 @@ def create_job(
141
144
  """
142
145
  Create and start a new job.
143
146
  """
147
+ allowed_success, allowed_msg = is_user_allowed_to_execute(curr_user, debug=debug)
148
+ if not allowed_success:
149
+ return allowed_success, allowed_msg
150
+
144
151
  sysargs = metadata if isinstance(metadata, list) else metadata['sysargs']
145
152
  properties = metadata['properties'] if isinstance(metadata, dict) else None
146
153
  job = Job(
@@ -172,6 +179,9 @@ def delete_job(
172
179
  """
173
180
  Delete a job.
174
181
  """
182
+ allowed_success, allowed_msg = is_user_allowed_to_execute(curr_user, debug=debug)
183
+ if not allowed_success:
184
+ return allowed_success, allowed_msg
175
185
  job = _get_job(name)
176
186
  return job.delete()
177
187
 
@@ -221,6 +231,10 @@ def start_job(
221
231
  """
222
232
  Start a job if stopped.
223
233
  """
234
+ allowed_success, allowed_msg = is_user_allowed_to_execute(curr_user, debug=debug)
235
+ if not allowed_success:
236
+ return allowed_success, allowed_msg
237
+
224
238
  job = _get_job(name)
225
239
  if not job.exists():
226
240
  raise fastapi.HTTPException(
@@ -240,6 +254,10 @@ def stop_job(
240
254
  """
241
255
  Stop a job if running.
242
256
  """
257
+ allowed_success, allowed_msg = is_user_allowed_to_execute(curr_user, debug=debug)
258
+ if not allowed_success:
259
+ return allowed_success, allowed_msg
260
+
243
261
  job = _get_job(name)
244
262
  if not job.exists():
245
263
  raise fastapi.HTTPException(
@@ -259,6 +277,10 @@ def pause_job(
259
277
  """
260
278
  Pause a job if running.
261
279
  """
280
+ allowed_success, allowed_msg = is_user_allowed_to_execute(curr_user, debug=debug)
281
+ if not allowed_success:
282
+ return allowed_success, allowed_msg
283
+
262
284
  job = _get_job(name)
263
285
  if not job.exists():
264
286
  raise fastapi.HTTPException(
@@ -7,24 +7,24 @@ Manage access and refresh tokens.
7
7
  """
8
8
 
9
9
  from datetime import datetime, timedelta, timezone
10
+
10
11
  import fastapi
11
12
  from fastapi import Request, status
12
13
  from fastapi_login.exceptions import InvalidCredentialsException
13
14
  from fastapi.exceptions import RequestValidationError
14
- from starlette.responses import Response, JSONResponse
15
+ from starlette.responses import JSONResponse
16
+
15
17
  from meerschaum.api import endpoints, get_api_connector, app, debug, manager, no_auth
16
18
  from meerschaum.core import User
17
19
  from meerschaum.config.static import STATIC_CONFIG
18
- from meerschaum.utils.typing import Dict, Any, Optional
20
+ from meerschaum.utils.typing import Dict, Any
19
21
  from meerschaum.core.User._User import verify_password
20
22
  from meerschaum.utils.warnings import warn
21
23
  from meerschaum.api._oauth2 import CustomOAuth2PasswordRequestForm
22
24
 
23
25
 
24
26
  @manager.user_loader()
25
- def load_user(
26
- username: str
27
- ) -> User:
27
+ def load_user(username: str) -> User:
28
28
  """
29
29
  Create the `meerschaum.core.User` object from the username.
30
30
  """
@@ -56,8 +56,8 @@ def login(
56
56
  expires_delta = timedelta(minutes=expires_minutes)
57
57
  expires_dt = datetime.now(timezone.utc).replace(tzinfo=None) + expires_delta
58
58
  access_token = manager.create_access_token(
59
- data = {'sub': username},
60
- expires = expires_delta
59
+ data={'sub': username},
60
+ expires=expires_delta
61
61
  )
62
62
  return {
63
63
  'access_token': access_token,
@@ -73,6 +73,6 @@ async def validation_exception_handler(request: Request, exc: RequestValidationE
73
73
  """
74
74
  warn(f"Validation error: {exc.errors()}", stack=False)
75
75
  return JSONResponse(
76
- status_code = status.HTTP_422_UNPROCESSABLE_ENTITY,
77
- content = {"detail": exc.errors()},
76
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
77
+ content={"detail": exc.errors()},
78
78
  )
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "2.6.11"
5
+ __version__ = "2.6.13"
@@ -50,10 +50,15 @@ def register_pipe(
50
50
  )
51
51
  if debug:
52
52
  dprint(response.text)
53
- if isinstance(response.json(), list):
54
- response_tuple = response.__bool__(), response.json()[1]
53
+
54
+ if not response:
55
+ return False, response.text
56
+
57
+ response_data = response.json()
58
+ if isinstance(response_data, list):
59
+ response_tuple = response_data[0], response_data[1]
55
60
  elif 'detail' in response.json():
56
- response_tuple = response.__bool__(), response.json()['detail']
61
+ response_tuple = response.__bool__(), response_data['detail']
57
62
  else:
58
63
  response_tuple = response.__bool__(), response.text
59
64
  return response_tuple
@@ -80,10 +85,13 @@ def edit_pipe(
80
85
  )
81
86
  if debug:
82
87
  dprint(response.text)
88
+
89
+ response_data = response.json()
90
+
83
91
  if isinstance(response.json(), list):
84
- response_tuple = response.__bool__(), response.json()[1]
92
+ response_tuple = response_data[0], response_data[1]
85
93
  elif 'detail' in response.json():
86
- response_tuple = response.__bool__(), response.json()['detail']
94
+ response_tuple = response.__bool__(), response_data['detail']
87
95
  else:
88
96
  response_tuple = response.__bool__(), response.text
89
97
  return response_tuple
@@ -318,10 +326,12 @@ def delete_pipe(
318
326
  )
319
327
  if debug:
320
328
  dprint(response.text)
329
+
330
+ response_data = response.json()
321
331
  if isinstance(response.json(), list):
322
- response_tuple = response.__bool__(), response.json()[1]
332
+ response_tuple = response_data[0], response_data[1]
323
333
  elif 'detail' in response.json():
324
- response_tuple = response.__bool__(), response.json()['detail']
334
+ response_tuple = response.__bool__(), response_data['detail']
325
335
  else:
326
336
  response_tuple = response.__bool__(), response.text
327
337
  return response_tuple
@@ -637,7 +647,7 @@ def drop_pipe(
637
647
  return False, f"Failed to drop {pipe}."
638
648
 
639
649
  if isinstance(data, list):
640
- response_tuple = response.__bool__(), data[1]
650
+ response_tuple = data[0], data[1]
641
651
  elif 'detail' in response.json():
642
652
  response_tuple = response.__bool__(), data['detail']
643
653
  else:
@@ -11,6 +11,8 @@ import os
11
11
  import hashlib
12
12
  import hmac
13
13
  from binascii import b2a_base64, a2b_base64, Error as _BinAsciiError
14
+
15
+ import meerschaum as mrsm
14
16
  from meerschaum.utils.typing import Optional, Dict, Any, Union
15
17
  from meerschaum.config.static import STATIC_CONFIG
16
18
  from meerschaum.utils.warnings import warn
@@ -18,6 +20,7 @@ from meerschaum.utils.warnings import warn
18
20
 
19
21
  __all__ = ('hash_password', 'verify_password', 'User')
20
22
 
23
+
21
24
  def hash_password(
22
25
  password: str,
23
26
  salt: Optional[bytes] = None,
@@ -113,9 +116,11 @@ _BASE64_STRIP = b"=\n"
113
116
  _BASE64_PAD1 = b"="
114
117
  _BASE64_PAD2 = b"=="
115
118
 
119
+
116
120
  def ab64_encode(data):
117
121
  return b64s_encode(data).replace(b"+", b".")
118
122
 
123
+
119
124
  def ab64_decode(data):
120
125
  """
121
126
  decode from shortened base64 format which omits padding & whitespace.
@@ -133,6 +138,7 @@ def ab64_decode(data):
133
138
  def b64s_encode(data):
134
139
  return b2a_base64(data).rstrip(_BASE64_STRIP)
135
140
 
141
+
136
142
  def b64s_decode(data):
137
143
  """
138
144
  decode from shortened base64 format which omits padding & whitespace.
@@ -222,5 +228,6 @@ class User:
222
228
  _password_hash = self.__dict__.get('_password_hash', None)
223
229
  if _password_hash is not None:
224
230
  return _password_hash
231
+
225
232
  self._password_hash = hash_password(self.password)
226
233
  return self._password_hash
@@ -6,4 +6,33 @@
6
6
  Manager users' metadata via the User class
7
7
  """
8
8
 
9
+ from typing import Optional
10
+
11
+ import meerschaum as mrsm
9
12
  from meerschaum.core.User._User import User, hash_password, verify_password
13
+
14
+
15
+ def is_user_allowed_to_execute(
16
+ user: Optional[User],
17
+ debug: bool = False,
18
+ ) -> mrsm.SuccessTuple:
19
+ """
20
+ Return a `SuccessTuple` indicating whether a given user is allowed to execute actions.
21
+ """
22
+ print(f"{debug=}")
23
+ print(f"{user=}")
24
+ if user is None:
25
+ return True, "Success"
26
+
27
+ user_type = user.instance_connector.get_user_type(user, debug=debug)
28
+
29
+ if user_type == 'admin':
30
+ return True, "Success"
31
+
32
+ from meerschaum.config import get_config
33
+
34
+ allow_non_admin = get_config('system', 'api', 'permissions', 'actions', 'non_admin')
35
+ if not allow_non_admin:
36
+ return False, "The administrator for this server has not allowed users to perform actions."
37
+
38
+ return True, "Success"
@@ -315,7 +315,13 @@ class Daemon:
315
315
  with open(self.pid_path, 'w+', encoding='utf-8') as f:
316
316
  f.write(str(os.getpid()))
317
317
 
318
- self._log_refresh_timer.start()
318
+ ### NOTE: The timer fails to start for remote actions to localhost.
319
+ try:
320
+ if not self._log_refresh_timer.is_running():
321
+ self._log_refresh_timer.start()
322
+ except Exception:
323
+ pass
324
+
319
325
  self.properties['result'] = None
320
326
  self._capture_process_timestamp('began')
321
327
  result = self.target(*self.target_args, **self.target_kw)
@@ -345,7 +351,7 @@ class Daemon:
345
351
  except BrokenPipeError:
346
352
  pass
347
353
 
348
- except Exception as e:
354
+ except Exception:
349
355
  daemon_error = traceback.format_exc()
350
356
  with open(DAEMON_ERROR_LOG_PATH, 'a+', encoding='utf-8') as f:
351
357
  f.write(daemon_error)
@@ -152,6 +152,7 @@ packages['dash'] = {
152
152
  'dash_bootstrap_components' : 'dash-bootstrap-components>=1.2.1',
153
153
  'dash_ace' : 'dash-ace>=0.2.1',
154
154
  'dash_extensions' : 'dash-extensions>=1.0.4',
155
+ 'dataclass_wizard' : 'dataclass-wizard>=0.28.0',
155
156
  'dash_daq' : 'dash-daq>=0.5.0',
156
157
  'terminado' : 'terminado>=0.12.1',
157
158
  'tornado' : 'tornado>=6.1.0',
@@ -10,7 +10,6 @@ from __future__ import annotations
10
10
  from meerschaum.utils.typing import Optional
11
11
 
12
12
  import threading
13
- import traceback
14
13
  Lock = threading.Lock
15
14
  RLock = threading.RLock
16
15
  Event = threading.Event
@@ -67,8 +66,8 @@ class Worker(threading.Thread):
67
66
  def run(self):
68
67
  while True:
69
68
  try:
70
- item = self.queue.get(timeout=self.timeout)
71
- except queue.Empty:
69
+ _ = self.queue.get(timeout=self.timeout)
70
+ except self.queue.Empty:
72
71
  return None
73
72
 
74
73
  self.queue.task_done()
@@ -79,9 +78,21 @@ class RepeatTimer(Timer):
79
78
  Fire the timer's target function in a loop, every `interval` seconds.
80
79
  """
81
80
 
81
+ def __init__(self, *args, **kwargs):
82
+ super().__init__(*args, **kwargs)
83
+ self._is_running = False
84
+
85
+ def is_running(self) -> bool:
86
+ """
87
+ Return whether this timer has been started and is running.
88
+ """
89
+ return self._is_running
90
+
82
91
  def run(self) -> None:
83
92
  """
84
93
  Fire the target function in a loop.
85
94
  """
95
+ self._is_running = True
86
96
  while not self.finished.wait(self.interval):
87
97
  self.function(*self.args, **self.kwargs)
98
+ self._is_running = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: meerschaum
3
- Version: 2.6.11
3
+ Version: 2.6.13
4
4
  Summary: Sync Time-Series Pipes with Meerschaum
5
5
  Home-page: https://meerschaum.io
6
6
  Author: Bennett Meares
@@ -20,7 +20,6 @@ Classifier: Operating System :: POSIX :: Linux
20
20
  Classifier: Operating System :: Microsoft :: Windows
21
21
  Classifier: Operating System :: MacOS
22
22
  Classifier: Programming Language :: SQL
23
- Classifier: Programming Language :: Python :: 3.8
24
23
  Classifier: Programming Language :: Python :: 3.9
25
24
  Classifier: Programming Language :: Python :: 3.10
26
25
  Classifier: Programming Language :: Python :: 3.11
@@ -96,6 +95,7 @@ Requires-Dist: dash >=2.6.2 ; extra == 'api'
96
95
  Requires-Dist: dash-bootstrap-components >=1.2.1 ; extra == 'api'
97
96
  Requires-Dist: dash-ace >=0.2.1 ; extra == 'api'
98
97
  Requires-Dist: dash-extensions >=1.0.4 ; extra == 'api'
98
+ Requires-Dist: dataclass-wizard >=0.28.0 ; extra == 'api'
99
99
  Requires-Dist: dash-daq >=0.5.0 ; extra == 'api'
100
100
  Requires-Dist: terminado >=0.12.1 ; extra == 'api'
101
101
  Requires-Dist: tornado >=6.1.0 ; extra == 'api'
@@ -141,6 +141,7 @@ Requires-Dist: dash >=2.6.2 ; extra == 'dash'
141
141
  Requires-Dist: dash-bootstrap-components >=1.2.1 ; extra == 'dash'
142
142
  Requires-Dist: dash-ace >=0.2.1 ; extra == 'dash'
143
143
  Requires-Dist: dash-extensions >=1.0.4 ; extra == 'dash'
144
+ Requires-Dist: dataclass-wizard >=0.28.0 ; extra == 'dash'
144
145
  Requires-Dist: dash-daq >=0.5.0 ; extra == 'dash'
145
146
  Requires-Dist: terminado >=0.12.1 ; extra == 'dash'
146
147
  Requires-Dist: tornado >=6.1.0 ; extra == 'dash'
@@ -251,6 +252,7 @@ Requires-Dist: dash >=2.6.2 ; extra == 'full'
251
252
  Requires-Dist: dash-bootstrap-components >=1.2.1 ; extra == 'full'
252
253
  Requires-Dist: dash-ace >=0.2.1 ; extra == 'full'
253
254
  Requires-Dist: dash-extensions >=1.0.4 ; extra == 'full'
255
+ Requires-Dist: dataclass-wizard >=0.28.0 ; extra == 'full'
254
256
  Requires-Dist: dash-daq >=0.5.0 ; extra == 'full'
255
257
  Requires-Dist: terminado >=0.12.1 ; extra == 'full'
256
258
  Requires-Dist: tornado >=6.1.0 ; extra == 'full'
@@ -22,7 +22,7 @@ meerschaum/_internal/term/TermPageHandler.py,sha256=57peuFD4geabiP7aomVNH_bRfNFP
22
22
  meerschaum/_internal/term/__init__.py,sha256=AT-dUw6cPISDDvHUplrSMsmPR5mo2JgVTtIzmOGcW7E,1733
23
23
  meerschaum/_internal/term/tools.py,sha256=dXVAimKD-Yv2fg2WOTr0YGBY7XDKjQqw-RizcS65YVI,727
24
24
  meerschaum/actions/__init__.py,sha256=MHPs8aRBhbZQXnqd_6tVtisTrNCgPAPgnNcXYbn0zP8,11640
25
- meerschaum/actions/api.py,sha256=xeqkf4S-DEzFR8roIF1mzy-i_mAnUPkF7y3nIu8twCo,12593
25
+ meerschaum/actions/api.py,sha256=41r3fBh3vAPyNaOrvbh2oh6WUJTR2I-zaOEZN60A66E,12538
26
26
  meerschaum/actions/attach.py,sha256=UV19d9W_2WYcrf7BRz7k3mriDoX1V4rA4AKvbLdor0o,3106
27
27
  meerschaum/actions/bootstrap.py,sha256=9y4HU1uqjbkAg_706pOOJUMzl196pU7ghfGDCWg0vPE,18234
28
28
  meerschaum/actions/clear.py,sha256=tMacHFv8btWpkNnXHtKDOMiCDNhGb5S6CJhCDIrrNDk,4914
@@ -56,13 +56,13 @@ meerschaum/api/_chain.py,sha256=h8-WXUGXX6AqzdALfsBC5uv0FkAcLdHJXCGzqzuq89k,875
56
56
  meerschaum/api/_events.py,sha256=f-98AXHU10IL9zRGX1FrZFANxxiMz5ryeJnfFWaU8R8,2232
57
57
  meerschaum/api/_oauth2.py,sha256=dJTIVlPpX3sAVW-PcN6pXRNy2RR5QAalu2RHp3l14YU,1683
58
58
  meerschaum/api/_websockets.py,sha256=EMT9wB3yELu_WyCMqn9ZpgMDh23spUUchouRLCCLVuw,1509
59
- meerschaum/api/dash/__init__.py,sha256=Qs7Q1wMI3TWl1gRECgHyJpPb7Zh70F_6TLIF8_qDv2s,2023
59
+ meerschaum/api/dash/__init__.py,sha256=29vMm_m5gSDYG0lahh-8yVfhqg9kUFnUrYyw_9jC2Y0,2078
60
60
  meerschaum/api/dash/components.py,sha256=t2goHW7oioao5Ew6Dro9U4LZDnHF-YWb4flLPx46GP8,6293
61
61
  meerschaum/api/dash/connectors.py,sha256=nJxBOFldtCMJLYjUSVYZwX5BO-LNjTNHgoEaXe-0XMo,843
62
62
  meerschaum/api/dash/graphs.py,sha256=wJUDWzcLN8-C3xko6rj0F2v7Rt8YDkSXoVkkXJjYGIk,2046
63
63
  meerschaum/api/dash/jobs.py,sha256=mj9STE6AaQY4fwkjD1JcYRG0iW3VEcP04bO1SlKgiXw,7681
64
64
  meerschaum/api/dash/keys.py,sha256=hzEVeN60SAfVTVSO5lajGaykxRIKGhj9Ph00HRJnNoE,12598
65
- meerschaum/api/dash/pipes.py,sha256=TyJHPTKQCAmuqnbYv8kNrl6KpVIFTsgXKVs0jgSHimw,25593
65
+ meerschaum/api/dash/pipes.py,sha256=39Ler1YIxaDmbZu9zKuJclA-j9psbrPRwZfBnh4KuxA,26264
66
66
  meerschaum/api/dash/plugins.py,sha256=KdfG04f6SsUpBg-nm7MUJegFGuElOj-GAkxDX98hi60,3768
67
67
  meerschaum/api/dash/sessions.py,sha256=-y5p4MYKh1eFzppkBfMsd6T7-rJs1nYS2-4fbVRAeRA,5029
68
68
  meerschaum/api/dash/sync.py,sha256=9lt7IRdG-fe9gf_ZO_viPiGlerX7ic6r_VFocv3I51A,504
@@ -77,7 +77,7 @@ meerschaum/api/dash/assets/logo_48x48.png,sha256=hTR5BHUHEN4yP2xiqAcDciuigoII9T3
77
77
  meerschaum/api/dash/assets/logo_500x500.png,sha256=9EUtf6wQcEZTXHKfQ2kjNXod6Rn_4DTB_BkTgxggq00,67702
78
78
  meerschaum/api/dash/callbacks/__init__.py,sha256=5nLDkziaWWWt5ivmuMNG3kVBMOfqB6KQNIAS8f16bmA,493
79
79
  meerschaum/api/dash/callbacks/custom.py,sha256=N9pVolAF8sIuJD3V6xBSgS7k8THJo_f8d1qAoh1Kg60,1161
80
- meerschaum/api/dash/callbacks/dashboard.py,sha256=aXZ22qcVq_MOn2GWBuFV1MUjmojLjyDk24QyaUnkkk4,32757
80
+ meerschaum/api/dash/callbacks/dashboard.py,sha256=CWIHQAIBm5zORQXevpaeoA-AvHi0XJLkyj2tNMdI3EY,32745
81
81
  meerschaum/api/dash/callbacks/jobs.py,sha256=JYTrDcUEte_MIT3EegLDmQDsmU_Mxqw8L60dvF71ho4,8418
82
82
  meerschaum/api/dash/callbacks/login.py,sha256=mEvMgV-f85H6DvqNdTvJPoiwHqTnhWY2nf_zLB26ipE,2876
83
83
  meerschaum/api/dash/callbacks/pipes.py,sha256=byphQn-wJOe8ft-fGU9wac0n5xsMjVHJzNvYYb9NsKU,1693
@@ -118,11 +118,11 @@ meerschaum/api/resources/templates/old_index.html,sha256=BDeOlcXhSsBH3-NaRtuX4Z1
118
118
  meerschaum/api/resources/templates/secret.html,sha256=0QWkm4ZoN81Aw1pd2-62rGCvx3nXPHfFUoegj3Iy8Ls,141
119
119
  meerschaum/api/resources/templates/termpage.html,sha256=qspXRuOkzqOn2mXw9mmUldzsvOHq_LyaywQ29CUevp0,4527
120
120
  meerschaum/api/routes/__init__.py,sha256=jbkeFNl51Tg8aT5gWe560ZLZLojFJsLMe5IENRjRkb0,606
121
- meerschaum/api/routes/_actions.py,sha256=KGUlPyWiO0e7t7Fj8-nIowWokDVP22Gr9z1XxLx5tpw,3063
121
+ meerschaum/api/routes/_actions.py,sha256=VUasS1dpr4d3TXHcR1CXlRZPAqvGKKuHv_f9PsOkQ5c,1732
122
122
  meerschaum/api/routes/_connectors.py,sha256=NNbcn5xWhKqw2PqueSEaqRaZ95hFGDKazG5lE7gsssc,1849
123
123
  meerschaum/api/routes/_index.py,sha256=QI6CBo6pI2Zi0a6fJHDjZfiLa9f4okb0BGe3A_JD0kM,578
124
- meerschaum/api/routes/_jobs.py,sha256=deKv9Y9XtCAB6u3ZyI2_zeMbrzkA8Q8-o6PWmTf9sxc,11702
125
- meerschaum/api/routes/_login.py,sha256=ti2onNOemOGLHLoPubAQOYtD7eq7FA1jOlbOSVSjXVo,2466
124
+ meerschaum/api/routes/_jobs.py,sha256=sEt-UtVd5pN-hJgikTvj1oTKJQ2hhNe8XhjkclwOXOE,12568
125
+ meerschaum/api/routes/_login.py,sha256=tygEp50EVOMgvTG6CEASlShflbtEK8viJ9O07o0lnnE,2434
126
126
  meerschaum/api/routes/_misc.py,sha256=05--9ZVFeaCgZrHER2kA3SYdK4TyfkEXOCjLvPbum-w,2469
127
127
  meerschaum/api/routes/_pipes.py,sha256=li52WQUwZN8JEPyIuWN7s4wASWNV2BTVAUpQ_E1CKLg,21579
128
128
  meerschaum/api/routes/_plugins.py,sha256=vR6-uTJraY1YEJMuRvds1-xFLB2mexxnp2dJwN_0rVo,6216
@@ -143,7 +143,7 @@ meerschaum/config/_preprocess.py,sha256=-AEA8m_--KivZwTQ1sWN6LTn5sio_fUr2XZ51BO6
143
143
  meerschaum/config/_read_config.py,sha256=RLC3HHi_1ndj7ITVDKLD9_uULY3caGRwSz3ATYE-ixA,15014
144
144
  meerschaum/config/_shell.py,sha256=46_m49Txc5q1rGfCgO49ca48BODx45DQJi8D0zz1R18,4245
145
145
  meerschaum/config/_sync.py,sha256=jHcWRkxd82_BgX8Xo8agsWvf7BSbv3qHLWmYl6ehp_0,4242
146
- meerschaum/config/_version.py,sha256=9mioAgrA45wea21B52E0dg50FCIZXQtru_nNS831CXo,72
146
+ meerschaum/config/_version.py,sha256=Ye56ah-9dP1YgHaQXApA4ezZi9HYTnZKyxqKSOsLtPk,72
147
147
  meerschaum/config/paths.py,sha256=JjibeGN3YAdSNceRwsd42aNmeUrIgM6ndzC8qZAmNI0,621
148
148
  meerschaum/config/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
149
149
  meerschaum/config/stack/__init__.py,sha256=2UukC0Lmk-aVL1o1qXzumqmuIrw3vu9fD7iCuz4XD4I,10544
@@ -163,7 +163,7 @@ meerschaum/connectors/api/_fetch.py,sha256=Khq9AFr1nk8Dsmcedb77aWhAuHw0JGgVeahDG
163
163
  meerschaum/connectors/api/_jobs.py,sha256=N5lpHFGG10jlVgaJeWAOTuLBQw3AdgjXsEPpp1YwZQE,11270
164
164
  meerschaum/connectors/api/_login.py,sha256=5GsD-B214vr5EYfM3XrTUs1sTFApxZA-9dNxq8oNSyg,2050
165
165
  meerschaum/connectors/api/_misc.py,sha256=OZRZBYOokKIEjmQaR8jUYgu6ZRn9VzXBChzR8CfDv_w,1092
166
- meerschaum/connectors/api/_pipes.py,sha256=wHPfIqiJyU3iEQvEW61cFlriQVBvgx6hTL7VjLRgv2Q,21308
166
+ meerschaum/connectors/api/_pipes.py,sha256=ceY-6wlmq73iPvYbvWz59xTUMkSoiiXCiz_BWjTZVMk,21443
167
167
  meerschaum/connectors/api/_plugins.py,sha256=z04tPjfZZWwa7T60mogZH3X3wDmeLdnoN5Oh8m_YsU8,5217
168
168
  meerschaum/connectors/api/_request.py,sha256=Wc4Y70t0VxEj3_ch5fAeeoSAeFMuRnyNLOV-aUFInjY,6996
169
169
  meerschaum/connectors/api/_uri.py,sha256=HWxqGx4R1cHZ3ywy9Ro9ePbFxxusw4RLaC3hpGt9Z6I,1469
@@ -208,8 +208,8 @@ meerschaum/core/Pipe/_show.py,sha256=nG50y8eBT9TVuKkRgAKtNDNIxysJvMNxfu__lkL1F9k
208
208
  meerschaum/core/Pipe/_sync.py,sha256=E2egt63tqhVpB0ZteMO36HTF4EYEMtdevKOtN1aLd9o,34753
209
209
  meerschaum/core/Pipe/_verify.py,sha256=c3HvsZd4QPydqozaV6cDDRtwYiNz4V91b0IcnKvcimA,14158
210
210
  meerschaum/core/Plugin/__init__.py,sha256=UXg64EvJPgI1PCxkY_KM02-ZmBm4FZpLPIQR_uSJJDc,137
211
- meerschaum/core/User/_User.py,sha256=JZ9Y1tsjZe-cgD24m9YfZ6ZwSOKn_sHc4rbQ7KblBz8,6592
212
- meerschaum/core/User/__init__.py,sha256=lJ7beIZTG9sO4dAi3367fFBl17dXYEWHKi7HoaPlDyk,193
211
+ meerschaum/core/User/_User.py,sha256=qbI0GIkr3G0PI4d9S49uatbJQ2kH_-z5-GoVJ0fuEtA,6624
212
+ meerschaum/core/User/__init__.py,sha256=9qNy-Gobui4x6GiaE8U7-WOggsdniOM3_wegLN3SVKs,988
213
213
  meerschaum/jobs/_Executor.py,sha256=qM62BhFTM4tyJ7p90KOM0y3qyeRY9k3ZV_aTDJMHnO8,1682
214
214
  meerschaum/jobs/_Job.py,sha256=hkjHqG5YPej5rC4nwdU7Ay_mjep9Fy4HO5B-eqdBdfo,31984
215
215
  meerschaum/jobs/__init__.py,sha256=q0f_2zWw91sAyafP50IgMM06abe-BIYSR_SCWmI4W3E,12177
@@ -229,11 +229,11 @@ meerschaum/utils/process.py,sha256=9O8PPPJjY9Q5W2f39I3B3lFU6TlSiRiI3bgrzdOOyOw,7
229
229
  meerschaum/utils/prompt.py,sha256=6J--mZJ_NcEdSX6KMjtY4fXXezyILLHP24VdxFFqOIc,18985
230
230
  meerschaum/utils/schedule.py,sha256=9BQGEzDbInLAU1aFO-FvL3wKu9XCTUpS0V_aQID6xzc,11228
231
231
  meerschaum/utils/sql.py,sha256=TAsHcUXg2RN3UfwMh3V8U5MtBrTn1VCIvikUAwsw3q4,72164
232
- meerschaum/utils/threading.py,sha256=3N8JXPAnwqJiSjuQcbbJg3Rv9-CCUMJpeQRfKFR7MaA,2489
232
+ meerschaum/utils/threading.py,sha256=awjbVL_QR6G-o_9Qk85utac9cSdqkiC8tQSdERCdrG8,2814
233
233
  meerschaum/utils/typing.py,sha256=U3MC347sh1umpa3Xr1k71eADyDmk4LB6TnVCpq8dVzI,2830
234
234
  meerschaum/utils/warnings.py,sha256=n-phr3BftNNgyPnvnXC_VMSjtCvjiCZ-ewmVfcROhkc,6611
235
235
  meerschaum/utils/yaml.py,sha256=PoC1du0pn2hLwTHwL-zuOf_EBWZSbCGOz-P-AZ4BWN0,3901
236
- meerschaum/utils/daemon/Daemon.py,sha256=rLUjJTGwKVUGVTHyz_A_lUJ_Wb2CgzT1ZFLhLwvGiNk,42142
236
+ meerschaum/utils/daemon/Daemon.py,sha256=Wj4IkcKb4NEgwPTSwf8_sKlKpbavlX5z4NPPcWnAS1k,42395
237
237
  meerschaum/utils/daemon/FileDescriptorInterceptor.py,sha256=MJKMO0Syf3d8yWUs6xXcQzg8Ptsuvh2aCRRoglOjusA,5257
238
238
  meerschaum/utils/daemon/RotatingFile.py,sha256=ePm_svjwyFDWh6V1k-bp1RHXCSWlyxDtlFu4SU4XvPU,24369
239
239
  meerschaum/utils/daemon/StdinFile.py,sha256=J6tyUReM8NEp3bBQAxMfe8mjJG5mWi6CzHN4x86VQBI,3237
@@ -247,15 +247,15 @@ meerschaum/utils/formatting/_pipes.py,sha256=840O5rg2aHhQoraCDOh2ZtBo43_W2W6R60y
247
247
  meerschaum/utils/formatting/_pprint.py,sha256=tgrT3FyGyu5CWJYysqK3kX1xdZYorlbOk9fcU_vt9Qg,3096
248
248
  meerschaum/utils/formatting/_shell.py,sha256=XH7VFLteNv7NGtWhJl7FdIGt80sKeTiDoJokGSDAwBM,3761
249
249
  meerschaum/utils/packages/__init__.py,sha256=Op93VJkAX3OL4H-js_p3dAaa_PT82jvjCna27aHOsUk,64199
250
- meerschaum/utils/packages/_packages.py,sha256=IFcQ4MzmTqjdWkqOsUa25xUNmG246TFqe2iZ0TCRPmI,8801
250
+ meerschaum/utils/packages/_packages.py,sha256=XTdIiPDDxcPqC0qEFveeEN4gmEjfwwWILQGhELP4IVQ,8868
251
251
  meerschaum/utils/packages/lazy_loader.py,sha256=VHnph3VozH29R4JnSSBfwtA5WKZYZQFT_GeQSShCnuc,2540
252
252
  meerschaum/utils/venv/_Venv.py,sha256=sBnlmxHdAh2bx8btfVoD79-H9-cYsv5lP02IIXkyECs,3553
253
253
  meerschaum/utils/venv/__init__.py,sha256=f3oi67lXYPLKJrnRW9lae7M3A8SFiC7DzaMoBdCVUFs,24609
254
- meerschaum-2.6.11.dist-info/LICENSE,sha256=jG2zQEdRNt88EgHUWPpXVWmOrOduUQRx7MnYV9YIPaw,11359
255
- meerschaum-2.6.11.dist-info/METADATA,sha256=TdB0ZGdW-VOkeadm3lq8YHwoQVzh6x5__gZq1rRHg8Q,24809
256
- meerschaum-2.6.11.dist-info/NOTICE,sha256=OTA9Fcthjf5BRvWDDIcBC_xfLpeDV-RPZh3M-HQBRtQ,114
257
- meerschaum-2.6.11.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
258
- meerschaum-2.6.11.dist-info/entry_points.txt,sha256=5YBVzibw-0rNA_1VjB16z5GABsOGf-CDhW4yqH8C7Gc,88
259
- meerschaum-2.6.11.dist-info/top_level.txt,sha256=bNoSiDj0El6buocix-FRoAtJOeq1qOF5rRm2u9i7Q6A,11
260
- meerschaum-2.6.11.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
261
- meerschaum-2.6.11.dist-info/RECORD,,
254
+ meerschaum-2.6.13.dist-info/LICENSE,sha256=jG2zQEdRNt88EgHUWPpXVWmOrOduUQRx7MnYV9YIPaw,11359
255
+ meerschaum-2.6.13.dist-info/METADATA,sha256=KtiOaApFspAVgL6VCe3DPJL2W8rI8B_T2ywEwHn0RWM,24935
256
+ meerschaum-2.6.13.dist-info/NOTICE,sha256=OTA9Fcthjf5BRvWDDIcBC_xfLpeDV-RPZh3M-HQBRtQ,114
257
+ meerschaum-2.6.13.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
258
+ meerschaum-2.6.13.dist-info/entry_points.txt,sha256=5YBVzibw-0rNA_1VjB16z5GABsOGf-CDhW4yqH8C7Gc,88
259
+ meerschaum-2.6.13.dist-info/top_level.txt,sha256=bNoSiDj0El6buocix-FRoAtJOeq1qOF5rRm2u9i7Q6A,11
260
+ meerschaum-2.6.13.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
261
+ meerschaum-2.6.13.dist-info/RECORD,,