meerschaum 2.7.9__py3-none-any.whl → 2.8.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. meerschaum/_internal/arguments/_parser.py +17 -5
  2. meerschaum/_internal/term/TermPageHandler.py +1 -1
  3. meerschaum/_internal/term/__init__.py +1 -1
  4. meerschaum/actions/api.py +36 -10
  5. meerschaum/actions/copy.py +3 -1
  6. meerschaum/actions/index.py +1 -1
  7. meerschaum/actions/show.py +7 -7
  8. meerschaum/actions/sync.py +5 -1
  9. meerschaum/actions/verify.py +14 -1
  10. meerschaum/api/__init__.py +77 -41
  11. meerschaum/api/_exceptions.py +18 -0
  12. meerschaum/api/dash/__init__.py +4 -2
  13. meerschaum/api/dash/callbacks/dashboard.py +30 -1
  14. meerschaum/api/dash/components.py +2 -2
  15. meerschaum/api/dash/webterm.py +23 -4
  16. meerschaum/api/models/_pipes.py +8 -8
  17. meerschaum/api/resources/static/css/dash.css +2 -2
  18. meerschaum/api/resources/templates/termpage.html +5 -1
  19. meerschaum/api/routes/__init__.py +15 -12
  20. meerschaum/api/routes/_connectors.py +30 -28
  21. meerschaum/api/routes/_index.py +16 -7
  22. meerschaum/api/routes/_misc.py +30 -22
  23. meerschaum/api/routes/_pipes.py +244 -148
  24. meerschaum/api/routes/_plugins.py +58 -47
  25. meerschaum/api/routes/_users.py +39 -31
  26. meerschaum/api/routes/_version.py +8 -10
  27. meerschaum/api/routes/_webterm.py +2 -2
  28. meerschaum/config/_default.py +10 -0
  29. meerschaum/config/_version.py +1 -1
  30. meerschaum/config/static/__init__.py +5 -2
  31. meerschaum/connectors/api/_APIConnector.py +4 -3
  32. meerschaum/connectors/api/_login.py +21 -17
  33. meerschaum/connectors/api/_pipes.py +1 -0
  34. meerschaum/connectors/api/_request.py +9 -10
  35. meerschaum/connectors/sql/_cli.py +11 -3
  36. meerschaum/connectors/sql/_instance.py +1 -1
  37. meerschaum/connectors/sql/_pipes.py +77 -57
  38. meerschaum/connectors/sql/_sql.py +26 -9
  39. meerschaum/core/Pipe/__init__.py +2 -0
  40. meerschaum/core/Pipe/_attributes.py +13 -2
  41. meerschaum/core/Pipe/_data.py +85 -0
  42. meerschaum/core/Pipe/_deduplicate.py +6 -8
  43. meerschaum/core/Pipe/_sync.py +63 -30
  44. meerschaum/core/Pipe/_verify.py +242 -77
  45. meerschaum/core/User/__init__.py +2 -6
  46. meerschaum/jobs/_Job.py +1 -1
  47. meerschaum/jobs/__init__.py +15 -0
  48. meerschaum/utils/dataframe.py +2 -0
  49. meerschaum/utils/dtypes/sql.py +26 -0
  50. meerschaum/utils/formatting/_pipes.py +1 -1
  51. meerschaum/utils/misc.py +11 -7
  52. meerschaum/utils/packages/_packages.py +1 -1
  53. meerschaum/utils/sql.py +6 -2
  54. {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/METADATA +4 -4
  55. {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/RECORD +61 -60
  56. {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/LICENSE +0 -0
  57. {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/NOTICE +0 -0
  58. {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/WHEEL +0 -0
  59. {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/entry_points.txt +0 -0
  60. {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/top_level.txt +0 -0
  61. {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/zip-safe +0 -0
@@ -7,41 +7,47 @@ Routes for managing plugins
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
- from meerschaum.utils.typing import Optional, List, SuccessTuple, Union, Any, Dict
10
+ import json
11
+ import shutil
12
+ import pathlib
13
+ import os
14
+
15
+ from meerschaum.utils.typing import Optional, List, SuccessTuple, Any, Dict
11
16
 
12
17
  from meerschaum.api import (
13
18
  fastapi,
14
19
  app,
15
20
  endpoints,
16
21
  get_api_connector,
17
- pipes,
18
- get_pipe,
19
22
  manager,
20
23
  debug,
21
- private, no_auth,
24
+ private,
25
+ no_auth,
26
+ default_instance_keys,
22
27
  )
23
- import fastapi
24
28
  from meerschaum.api.tables import get_tables
25
- from fastapi import FastAPI, File, UploadFile
29
+ from fastapi import File, UploadFile
26
30
  from meerschaum.utils.packages import attempt_import
27
- import meerschaum.core
28
31
  from meerschaum.core import Plugin
29
- starlette_responses = attempt_import('starlette.responses', warn=False)
32
+ starlette_responses = attempt_import('starlette.responses', warn=False, lazy=False)
30
33
  FileResponse = starlette_responses.FileResponse
31
34
 
32
35
  sqlalchemy = attempt_import('sqlalchemy', lazy=False)
33
36
  plugins_endpoint = endpoints['plugins']
34
37
 
38
+ PLUGINS_INSTANCE_KEYS = default_instance_keys
39
+
40
+
35
41
  @app.post(plugins_endpoint + '/{name}', tags=['Plugins'])
36
42
  def register_plugin(
37
- name: str,
38
- version: str = None,
39
- attributes: str = None,
40
- archive: UploadFile = File(...),
41
- curr_user = (
42
- fastapi.Depends(manager) if not no_auth else None
43
- ),
44
- ) -> SuccessTuple:
43
+ name: str,
44
+ version: str = None,
45
+ attributes: str = None,
46
+ archive: UploadFile = File(...),
47
+ curr_user = (
48
+ fastapi.Depends(manager) if not no_auth else None
49
+ ),
50
+ ) -> SuccessTuple:
45
51
  """
46
52
  Register a plugin and save its archive file.
47
53
 
@@ -78,7 +84,6 @@ def register_plugin(
78
84
  "you can toggle various registration types."
79
85
  )
80
86
 
81
- import json, shutil, pathlib, os
82
87
  get_tables()
83
88
  if attributes is None:
84
89
  attributes = json.dumps({})
@@ -86,7 +91,7 @@ def register_plugin(
86
91
  if isinstance(attributes, str) and attributes[0] == '{':
87
92
  try:
88
93
  attributes = json.loads(attributes)
89
- except Exception as e:
94
+ except Exception:
90
95
  pass
91
96
 
92
97
  plugin = Plugin(name, version=version, attributes=attributes)
@@ -97,13 +102,15 @@ def register_plugin(
97
102
  )
98
103
 
99
104
  if curr_user is not None:
100
- plugin_user_id = get_api_connector().get_plugin_user_id(plugin)
101
- curr_user_id = get_api_connector().get_user_id(curr_user) if curr_user is not None else -1
105
+ plugin_user_id = get_api_connector(PLUGINS_INSTANCE_KEYS).get_plugin_user_id(plugin)
106
+ curr_user_id = get_api_connector(PLUGINS_INSTANCE_KEYS).get_user_id(curr_user) if curr_user is not None else -1
102
107
  if plugin_user_id is not None and plugin_user_id != curr_user_id:
103
108
  return False, f"User '{curr_user.username}' cannot edit plugin '{plugin}'."
104
109
  plugin.user_id = curr_user_id
105
110
 
106
- success, msg = get_api_connector().register_plugin(plugin, make_archive=False, debug=debug)
111
+ success, msg = get_api_connector(
112
+ PLUGINS_INSTANCE_KEYS
113
+ ).register_plugin(plugin, make_archive=False, debug=debug)
107
114
 
108
115
  if success:
109
116
  archive_path = plugin.archive_path
@@ -117,11 +124,11 @@ def register_plugin(
117
124
 
118
125
  @app.get(plugins_endpoint + '/{name}', tags=['Plugins'])
119
126
  def get_plugin(
120
- name: str,
121
- curr_user = (
122
- fastapi.Depends(manager) if private else None
123
- ),
124
- ) -> Any:
127
+ name: str,
128
+ curr_user = (
129
+ fastapi.Depends(manager) if private else None
130
+ ),
131
+ ) -> Any:
125
132
  """
126
133
  Download a plugin's archive file.
127
134
  """
@@ -133,24 +140,25 @@ def get_plugin(
133
140
 
134
141
  @app.get(plugins_endpoint + '/{name}/attributes', tags=['Plugins'])
135
142
  def get_plugin_attributes(
136
- name: str,
137
- curr_user = (
138
- fastapi.Depends(manager) if private else None
139
- ),
140
- ) -> Dict[str, Any]:
143
+ name: str,
144
+ curr_user = (
145
+ fastapi.Depends(manager) if private else None
146
+ ),
147
+ ) -> Dict[str, Any]:
141
148
  """
142
149
  Get a plugin's attributes.
143
150
  """
144
- return get_api_connector().get_plugin_attributes(Plugin(name))
151
+ return get_api_connector(PLUGINS_INSTANCE_KEYS).get_plugin_attributes(Plugin(name))
152
+
145
153
 
146
154
  @app.get(plugins_endpoint, tags=['Plugins'])
147
155
  def get_plugins(
148
- user_id : Optional[int] = None,
149
- search_term : Optional[str] = None,
150
- curr_user = (
151
- fastapi.Depends(manager) if private else None
152
- ),
153
- ) -> List[str]:
156
+ user_id: Optional[int] = None,
157
+ search_term: Optional[str] = None,
158
+ curr_user = (
159
+ fastapi.Depends(manager) if private else None
160
+ ),
161
+ ) -> List[str]:
154
162
  """
155
163
  Get a list of plugins.
156
164
 
@@ -166,26 +174,29 @@ def get_plugins(
166
174
  -------
167
175
  A list of strings.
168
176
  """
169
- return get_api_connector().get_plugins(user_id=user_id, search_term=search_term)
177
+ return get_api_connector(
178
+ PLUGINS_INSTANCE_KEYS
179
+ ).get_plugins(user_id=user_id, search_term=search_term)
180
+
170
181
 
171
182
  @app.delete(plugins_endpoint + '/{name}', tags=['Plugins'])
172
183
  def delete_plugin(
173
- name : str,
174
- curr_user = (
175
- fastapi.Depends(manager) if private else None
176
- ),
177
- ) -> SuccessTuple:
184
+ name: str,
185
+ curr_user = (
186
+ fastapi.Depends(manager) if private else None
187
+ ),
188
+ ) -> SuccessTuple:
178
189
  """
179
190
  Delete a plugin and its archive file from the repository.
180
191
  """
181
192
  get_tables()
182
193
  plugin = Plugin(name)
183
- plugin_user_id = get_api_connector().get_plugin_user_id(plugin)
194
+ plugin_user_id = get_api_connector(PLUGINS_INSTANCE_KEYS).get_plugin_user_id(plugin)
184
195
  if plugin_user_id is None:
185
196
  return False, f"Plugin '{plugin}' is not registered."
186
197
 
187
198
  if curr_user is not None:
188
- curr_user_id = get_api_connector().get_user_id(curr_user)
199
+ curr_user_id = get_api_connector(PLUGINS_INSTANCE_KEYS).get_user_id(curr_user)
189
200
  if plugin_user_id != curr_user_id:
190
201
  return False, f"User '{curr_user.username}' cannot delete plugin '{plugin}'."
191
202
  else:
@@ -196,7 +207,7 @@ def delete_plugin(
196
207
  if not _remove_success[0]:
197
208
  return _remove_success
198
209
 
199
- _delete_success = get_api_connector().delete_plugin(plugin, debug=debug)
210
+ _delete_success = get_api_connector(PLUGINS_INSTANCE_KEYS).delete_plugin(plugin, debug=debug)
200
211
  if not _delete_success[0]:
201
212
  return _delete_success
202
213
 
@@ -16,7 +16,7 @@ from meerschaum.utils.packages import attempt_import
16
16
  from meerschaum.api import (
17
17
  fastapi, app, endpoints, get_api_connector, manager,
18
18
  debug, check_allow_chaining, DISALLOW_CHAINING_MESSAGE,
19
- no_auth, private,
19
+ no_auth, private, default_instance_keys,
20
20
  )
21
21
  from meerschaum.utils.misc import string_to_dict
22
22
  from meerschaum.config import get_config
@@ -28,31 +28,38 @@ users_endpoint = endpoints['users']
28
28
  import fastapi
29
29
  from fastapi import HTTPException, Form
30
30
 
31
+ USERS_INSTANCE_KEYS = default_instance_keys
32
+
31
33
 
32
34
  @app.get(users_endpoint + "/me", tags=['Users'])
33
35
  def read_current_user(
34
36
  curr_user = (
35
37
  fastapi.Depends(manager) if not no_auth else None
36
38
  ),
37
- ) -> Dict[str, Union[str, int]]:
39
+ ) -> Dict[str, Union[str, int, None, Dict[str, Any]]]:
38
40
  """
39
41
  Get information about the currently logged-in user.
40
42
  """
41
43
  return {
42
44
  'username': (
43
- curr_user.username if curr_user is not None else 'no_auth'
45
+ curr_user.username
46
+ if curr_user is not None
47
+ else 'no_auth'
44
48
  ),
45
49
  'user_id': (
46
- get_api_connector().get_user_id(curr_user)
47
- if curr_user is not None else -1
50
+ get_api_connector(USERS_INSTANCE_KEYS).get_user_id(curr_user)
51
+ if curr_user is not None
52
+ else -1
48
53
  ),
49
54
  'user_type': (
50
- get_api_connector().get_user_type(curr_user)
51
- if curr_user is not None else 'admin'
55
+ get_api_connector(USERS_INSTANCE_KEYS).get_user_type(curr_user)
56
+ if curr_user is not None
57
+ else 'admin'
52
58
  ),
53
59
  'attributes': (
54
- get_api_connector().get_user_attributes(curr_user)
55
- if curr_user is not None else {}
60
+ get_api_connector(USERS_INSTANCE_KEYS).get_user_attributes(curr_user)
61
+ if curr_user is not None
62
+ else {}
56
63
  ),
57
64
  }
58
65
 
@@ -66,7 +73,7 @@ def get_users(
66
73
  """
67
74
  Get a list of the registered users.
68
75
  """
69
- return get_api_connector().get_users(debug=debug)
76
+ return get_api_connector(USERS_INSTANCE_KEYS).get_users(debug=debug)
70
77
 
71
78
 
72
79
  @app.post(users_endpoint + "/register", tags=['Users'])
@@ -90,25 +97,25 @@ def register_user(
90
97
  try:
91
98
  attributes = string_to_dict(attributes)
92
99
  except Exception:
93
- return False, f"Invalid dictionary string received for attributes."
100
+ return False, "Invalid dictionary string received for attributes."
94
101
 
95
102
  allow_users = get_config('system', 'api', 'permissions', 'registration', 'users')
96
103
  if not allow_users:
97
104
  return False, (
98
105
  "The administrator for this server has not allowed user registration.\n\n"
99
- + "Please contact the system administrator, or if you are running this server, "
100
- + "open the configuration file with `edit config system` and search for 'permissions'. "
101
- + " Under the keys api:permissions:registration, "
102
- + "you can toggle various registration types."
106
+ "Please contact the system administrator, or if you are running this server, "
107
+ "open the configuration file with `edit config system` and search for 'permissions'. "
108
+ " Under the keys api:permissions:registration, "
109
+ "you can toggle various registration types."
103
110
  )
104
111
  if type == 'admin':
105
112
  return False, (
106
- "New users cannot be of type 'admin' when using the API connector. " +
107
- "Register a normal user first, then edit the user from an authorized account, " +
113
+ "New users cannot be of type 'admin' when using the API connector. "
114
+ "Register a normal user first, then edit the user from an authorized account, "
108
115
  "or use a SQL connector instead."
109
116
  )
110
117
  user = User(username, password, type=type, email=email, attributes=attributes)
111
- return get_api_connector().register_user(user, debug=debug)
118
+ return get_api_connector(USERS_INSTANCE_KEYS).register_user(user, debug=debug)
112
119
 
113
120
 
114
121
  @app.post(users_endpoint + "/edit", tags=['Users'])
@@ -129,21 +136,21 @@ def edit_user(
129
136
  try:
130
137
  attributes = string_to_dict(attributes)
131
138
  except Exception:
132
- return False, f"Invalid dictionary string received for attributes."
139
+ return False, "Invalid dictionary string received for attributes."
133
140
 
134
141
  user = User(username, password, email=email, attributes=attributes)
135
- user_type = get_api_connector().get_user_type(curr_user) if curr_user is not None else 'admin'
142
+ user_type = get_api_connector(USERS_INSTANCE_KEYS).get_user_type(curr_user) if curr_user is not None else 'admin'
136
143
  if user_type == 'admin' and type is not None:
137
144
  user.type = type
138
145
  if user_type == 'admin' or curr_user.username == user.username:
139
- return get_api_connector().edit_user(user, debug=debug)
146
+ return get_api_connector(USERS_INSTANCE_KEYS).edit_user(user, debug=debug)
140
147
 
141
148
  return False, f"Cannot edit user '{user}': Permission denied"
142
149
 
143
150
 
144
151
  @app.get(users_endpoint + "/{username}/id", tags=['Users'])
145
152
  def get_user_id(
146
- username : str,
153
+ username: str,
147
154
  curr_user = (
148
155
  fastapi.Depends(manager) if not no_auth else None
149
156
  ),
@@ -151,12 +158,12 @@ def get_user_id(
151
158
  """
152
159
  Get a user's ID.
153
160
  """
154
- return get_api_connector().get_user_id(User(username), debug=debug)
161
+ return get_api_connector(USERS_INSTANCE_KEYS).get_user_id(User(username), debug=debug)
155
162
 
156
163
 
157
164
  @app.get(users_endpoint + "/{username}/attributes", tags=['Users'])
158
165
  def get_user_attributes(
159
- username : str,
166
+ username: str,
160
167
  curr_user = (
161
168
  fastapi.Depends(manager) if private else None
162
169
  ),
@@ -164,7 +171,7 @@ def get_user_attributes(
164
171
  """
165
172
  Get a user's attributes.
166
173
  """
167
- return get_api_connector().get_user_attributes(User(username), debug=debug)
174
+ return get_api_connector(USERS_INSTANCE_KEYS).get_user_attributes(User(username), debug=debug)
168
175
 
169
176
 
170
177
  @app.delete(users_endpoint + "/{username}", tags=['Users'])
@@ -179,11 +186,12 @@ def delete_user(
179
186
  """
180
187
  user = User(username)
181
188
  user_type = (
182
- get_api_connector().get_user_type(curr_user, debug=debug)
183
- if curr_user is not None else 'admin'
189
+ get_api_connector(USERS_INSTANCE_KEYS).get_user_type(curr_user, debug=debug)
190
+ if curr_user is not None
191
+ else 'admin'
184
192
  )
185
193
  if user_type == 'admin' or curr_user.username == user.username:
186
- return get_api_connector().delete_user(user, debug=debug)
194
+ return get_api_connector(USERS_INSTANCE_KEYS).delete_user(user, debug=debug)
187
195
 
188
196
  return False, f"Cannot delete user '{user}': Permission denied"
189
197
 
@@ -203,12 +211,12 @@ def get_user_password_hash(
203
211
  """
204
212
  if not check_allow_chaining():
205
213
  raise HTTPException(status_code=403, detail=DISALLOW_CHAINING_MESSAGE)
206
- return get_api_connector().get_user_password_hash(User(username), debug=debug)
214
+ return get_api_connector(USERS_INSTANCE_KEYS).get_user_password_hash(User(username), debug=debug)
207
215
 
208
216
 
209
217
  @app.get(users_endpoint + '/{username}/type', tags=['Users'])
210
218
  def get_user_type(
211
- username : str,
219
+ username: str,
212
220
  curr_user = (
213
221
  fastapi.Depends(manager) if not no_auth else None
214
222
  ),
@@ -218,4 +226,4 @@ def get_user_type(
218
226
  """
219
227
  if not check_allow_chaining():
220
228
  raise HTTPException(status_code=403, detail=DISALLOW_CHAINING_MESSAGE)
221
- return get_api_connector().get_user_type(User(username))
229
+ return get_api_connector(USERS_INSTANCE_KEYS).get_user_type(User(username))
@@ -8,14 +8,13 @@ Return version information
8
8
 
9
9
  import fastapi
10
10
  from meerschaum.api import app, endpoints, private, manager
11
- from meerschaum.utils.typing import Union
12
11
 
13
12
  @app.get(endpoints['version'], tags=['Version'])
14
13
  def get_api_version(
15
- curr_user = (
16
- fastapi.Depends(manager) if private else None
17
- ),
18
- ):
14
+ curr_user = (
15
+ fastapi.Depends(manager) if private else None
16
+ ),
17
+ ):
19
18
  """
20
19
  Get the Meerschaum API version.
21
20
  """
@@ -24,13 +23,12 @@ def get_api_version(
24
23
 
25
24
  @app.get(endpoints['version'] + "/mrsm", tags=['Version'])
26
25
  def get_meerschaum_version(
27
- curr_user = (
28
- fastapi.Depends(manager) if private else None
29
- ),
30
- ):
26
+ curr_user = (
27
+ fastapi.Depends(manager) if private else None
28
+ ),
29
+ ):
31
30
  """
32
31
  Get the Meerschaum instance version.
33
32
  """
34
33
  from meerschaum import __version__ as version
35
34
  return version
36
-
@@ -76,7 +76,7 @@ async def get_webterm(
76
76
  text = response.text
77
77
  if request.url.scheme == 'https':
78
78
  text = text.replace('ws://', 'wss://')
79
- text = text.replace(f'_websocket/{username}', f'_websocket/{session_id}')
79
+ text = text.replace(f'websocket/{username}', f'websocket/{session_id}')
80
80
  return HTMLResponse(
81
81
  content=text,
82
82
  status_code=response.status_code,
@@ -100,7 +100,7 @@ async def webterm_websocket(websocket: WebSocket, session_id: str):
100
100
 
101
101
  username = get_username_from_session(session_id)
102
102
 
103
- ws_url = f"ws://localhost:8765/_websocket/{username or session_id}"
103
+ ws_url = f"ws://localhost:8765/websocket/{username or session_id}"
104
104
  async with websockets.connect(ws_url) as ws:
105
105
  async def forward_messages():
106
106
  try:
@@ -111,6 +111,12 @@ default_system_config = {
111
111
  'connector': 'valkey:main',
112
112
  'session_expires_minutes': 43200,
113
113
  },
114
+ 'data': {
115
+ 'max_response_row_limit': 100_000,
116
+ },
117
+ 'endpoints': {
118
+ 'docs_in_production': True,
119
+ },
114
120
  'permissions': {
115
121
  'registration': {
116
122
  'users': True,
@@ -124,6 +130,10 @@ default_system_config = {
124
130
  'insecure_parent_instance': False,
125
131
  'child_apis': False,
126
132
  },
133
+ 'instances': {
134
+ 'allow_multiple_instances': True,
135
+ 'allowed_instance_keys': ['*']
136
+ },
127
137
  },
128
138
  'protocol': default_meerschaum_config['connectors']['api']['default']['protocol'],
129
139
  },
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "2.7.9"
5
+ __version__ = "2.8.0"
@@ -7,7 +7,6 @@ Insert non-user-editable configuration files here.
7
7
  """
8
8
 
9
9
  import os
10
- import uuid
11
10
  from typing import Dict, Any
12
11
  from meerschaum.utils.misc import generate_password
13
12
 
@@ -33,9 +32,12 @@ STATIC_CONFIG: Dict[str, Any] = {
33
32
  'websocket': '/ws',
34
33
  'dash': '/dash',
35
34
  'webterm': r'/webterm/{session_id}',
36
- 'webterm_websocket': r'/_websocket/{session_id}',
35
+ 'webterm_websocket': r'/websocket/{session_id}',
37
36
  'info': '/info',
38
37
  'healthcheck': '/healthcheck',
38
+ 'docs': '/docs',
39
+ 'redoc': '/redoc',
40
+ 'openapi': '/openapi.json',
39
41
  },
40
42
  'oauth': {
41
43
  'token_expires_minutes': 720,
@@ -149,6 +151,7 @@ STATIC_CONFIG: Dict[str, Any] = {
149
151
  },
150
152
  'exists_timeout_seconds': 5.0,
151
153
  'static_schema_cache_seconds': 60.0,
154
+ 'max_bound_time_days': 36525,
152
155
  },
153
156
  'jobs': {
154
157
  'check_restart_seconds': 1.0,
@@ -169,12 +169,12 @@ class APIConnector(Connector):
169
169
  @property
170
170
  def session(self):
171
171
  if self._session is None:
172
- certifi = attempt_import('certifi', lazy=False)
172
+ _ = attempt_import('certifi', lazy=False)
173
173
  requests = attempt_import('requests', lazy=False)
174
174
  if requests:
175
175
  self._session = requests.Session()
176
176
  if self._session is None:
177
- error(f"Failed to import requests. Is requests installed?")
177
+ error("Failed to import requests. Is requests installed?")
178
178
  return self._session
179
179
 
180
180
  @property
@@ -191,6 +191,7 @@ class APIConnector(Connector):
191
191
 
192
192
  if self._token is None or expired:
193
193
  success, msg = self.login()
194
- if not success:
194
+ if not success and not self.__dict__.get('_emitted_warning'):
195
195
  warn(msg, stack=False)
196
+ self._emitted_warning = True
196
197
  return self._token
@@ -7,19 +7,21 @@ Log into the API instance or refresh the token.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
+
11
+ import json
12
+ import datetime
10
13
  from meerschaum.utils.typing import SuccessTuple, Any, Union
14
+ from meerschaum.config.static import STATIC_CONFIG
15
+ from meerschaum.utils.warnings import warn as _warn
16
+
11
17
 
12
18
  def login(
13
- self,
14
- debug: bool = False,
15
- warn: bool = True,
16
- **kw: Any
17
- ) -> SuccessTuple:
19
+ self,
20
+ debug: bool = False,
21
+ warn: bool = True,
22
+ **kw: Any
23
+ ) -> SuccessTuple:
18
24
  """Log in and set the session token."""
19
- from meerschaum.utils.warnings import warn as _warn, info, error
20
- from meerschaum.core import User
21
- from meerschaum.config.static import STATIC_CONFIG
22
- import json, datetime
23
25
  try:
24
26
  login_data = {
25
27
  'username': self.username,
@@ -27,11 +29,12 @@ def login(
27
29
  }
28
30
  except AttributeError:
29
31
  return False, f"Please login with the command `login {self}`."
32
+
30
33
  response = self.post(
31
34
  STATIC_CONFIG['api']['endpoints']['login'],
32
- data = login_data,
33
- use_token = False,
34
- debug = debug
35
+ data=login_data,
36
+ use_token=False,
37
+ debug=debug,
35
38
  )
36
39
  if response:
37
40
  msg = f"Successfully logged into '{self}' as user '{login_data['username']}'."
@@ -45,16 +48,17 @@ def login(
45
48
  f"Failed to log into '{self}' as user '{login_data['username']}'.\n" +
46
49
  f" Please verify login details for connector '{self}'."
47
50
  )
48
- if warn:
51
+ if warn and not self.__dict__.get('_emitted_warning', False):
49
52
  _warn(msg, stack=False)
53
+ self._emitted_warning = True
50
54
 
51
55
  return response.__bool__(), msg
52
56
 
53
57
 
54
58
  def test_connection(
55
- self,
56
- **kw: Any
57
- ) -> Union[bool, None]:
59
+ self,
60
+ **kw: Any
61
+ ) -> Union[bool, None]:
58
62
  """Test if a successful connection to the API may be made."""
59
63
  from meerschaum.connectors.poll import retry_connect
60
64
  _default_kw = {
@@ -65,5 +69,5 @@ def test_connection(
65
69
  _default_kw.update(kw)
66
70
  try:
67
71
  return retry_connect(**_default_kw)
68
- except Exception as e:
72
+ except Exception:
69
73
  return False
@@ -581,6 +581,7 @@ def get_pipe_rowcount(
581
581
  If provided, bound the count by these parameters.
582
582
 
583
583
  remote: bool, default False
584
+ If `True`, return the rowcount for the fetch definition.
584
585
 
585
586
  Returns
586
587
  -------
@@ -248,16 +248,15 @@ def delete(self, r_url: str, **kwargs: Any) -> 'requests.Response':
248
248
 
249
249
 
250
250
  def wget(
251
- self,
252
- r_url: str,
253
- dest: Optional[Union[str, pathlib.Path]] = None,
254
- headers: Optional[Dict[str, Any]] = None,
255
- use_token: bool = True,
256
- debug: bool = False,
257
- **kw: Any
258
- ) -> pathlib.Path:
259
- """Mimic wget with requests.
260
- """
251
+ self,
252
+ r_url: str,
253
+ dest: Optional[Union[str, pathlib.Path]] = None,
254
+ headers: Optional[Dict[str, Any]] = None,
255
+ use_token: bool = True,
256
+ debug: bool = False,
257
+ **kw: Any
258
+ ) -> pathlib.Path:
259
+ """Mimic wget with requests."""
261
260
  from meerschaum.utils.misc import wget
262
261
  if headers is None:
263
262
  headers = {}
@@ -109,13 +109,21 @@ def _cli_exit(
109
109
  ### and because `main.cli()` is not defined.
110
110
  launch_cli = f"cli_main.cli(['{cli_arg_str}'])"
111
111
  if self.flavor == 'mssql':
112
+ attrs = self.parse_uri(self.URI)
113
+ host = attrs.get('host', None)
114
+ port = attrs.get('port', None)
115
+ database = attrs.get('database', None)
116
+ username = attrs.get('username', None)
117
+ password = attrs.get('password', None)
118
+ if not host or not port or not database:
119
+ raise ValueError(f"Cannot determine attributes for '{self}'.")
112
120
  launch_cli = (
113
121
  "mssqlclioptionsparser, mssql_cli = attempt_import("
114
122
  + "'mssqlcli.mssqlclioptionsparser', 'mssqlcli.mssql_cli', lazy=False)\n"
115
123
  + "ms_parser = mssqlclioptionsparser.create_parser()\n"
116
- + f"ms_options = ms_parser.parse_args(['--server', 'tcp:{self.host},{self.port}', "
117
- + f"'--database', '{self.database}', "
118
- + f"'--username', '{self.username}', '--password', '{self.password}'])\n"
124
+ + f"ms_options = ms_parser.parse_args(['--server', 'tcp:{host},{port}', "
125
+ + f"'--database', '{database}', "
126
+ + f"'--username', '{username}', '--password', '{password}'])\n"
119
127
  + "ms_object = mssql_cli.MssqlCli(ms_options)\n"
120
128
  + "try:\n"
121
129
  + " ms_object.connect_to_database()\n"
@@ -186,7 +186,7 @@ def _drop_old_temporary_tables(
186
186
  ]
187
187
  if docs:
188
188
  queries = [sqlalchemy.insert(temp_tables_table).values(**doc) for doc in docs]
189
- results = [self.exec(query, silent=True, debug=debug) for query in queries]
189
+ _ = [self.exec(query, silent=True, debug=debug) for query in queries]
190
190
  _in_memory_temp_tables.update(
191
191
  {
192
192
  table: True