meerschaum 2.8.2__tar.gz → 2.8.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. {meerschaum-2.8.2/meerschaum.egg-info → meerschaum-2.8.4}/PKG-INFO +1 -1
  2. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/arguments/_parser.py +5 -0
  3. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/drop.py +1 -1
  4. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/start.py +14 -6
  5. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/sync.py +9 -0
  6. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/__init__.py +23 -11
  7. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/routes/_pipes.py +16 -22
  8. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/_default.py +3 -0
  9. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/_version.py +1 -1
  10. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/static/__init__.py +1 -1
  11. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/_SQLConnector.py +1 -1
  12. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/_cli.py +7 -1
  13. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/_create_engine.py +6 -3
  14. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/_users.py +4 -2
  15. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_verify.py +42 -18
  16. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/jobs/systemd.py +1 -1
  17. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/dataframe.py +1 -1
  18. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/packages/_packages.py +1 -1
  19. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/process.py +27 -3
  20. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/schedule.py +3 -3
  21. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/sql.py +1 -1
  22. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/venv/__init__.py +4 -1
  23. {meerschaum-2.8.2 → meerschaum-2.8.4/meerschaum.egg-info}/PKG-INFO +1 -1
  24. {meerschaum-2.8.2 → meerschaum-2.8.4}/LICENSE +0 -0
  25. {meerschaum-2.8.2 → meerschaum-2.8.4}/NOTICE +0 -0
  26. {meerschaum-2.8.2 → meerschaum-2.8.4}/README.md +0 -0
  27. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/__init__.py +0 -0
  28. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/__main__.py +0 -0
  29. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/__init__.py +0 -0
  30. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/arguments/__init__.py +0 -0
  31. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/arguments/_parse_arguments.py +0 -0
  32. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/docs/__init__.py +0 -0
  33. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/docs/index.py +0 -0
  34. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/entry.py +0 -0
  35. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/gui/__init__.py +0 -0
  36. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/gui/app/__init__.py +0 -0
  37. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/gui/app/_windows.py +0 -0
  38. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/gui/app/actions.py +0 -0
  39. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/gui/app/pipes.py +0 -0
  40. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/shell/Shell.py +0 -0
  41. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/shell/ShellCompleter.py +0 -0
  42. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/shell/ValidAutoSuggest.py +0 -0
  43. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/shell/__init__.py +0 -0
  44. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/shell/resources/__init__.py +0 -0
  45. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/shell/updates.py +0 -0
  46. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/term/TermPageHandler.py +0 -0
  47. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/term/__init__.py +0 -0
  48. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/_internal/term/tools.py +0 -0
  49. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/__init__.py +0 -0
  50. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/api.py +0 -0
  51. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/attach.py +0 -0
  52. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/bootstrap.py +0 -0
  53. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/clear.py +0 -0
  54. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/copy.py +0 -0
  55. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/deduplicate.py +0 -0
  56. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/delete.py +0 -0
  57. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/edit.py +0 -0
  58. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/index.py +0 -0
  59. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/install.py +0 -0
  60. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/login.py +0 -0
  61. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/os.py +0 -0
  62. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/pause.py +0 -0
  63. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/python.py +0 -0
  64. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/register.py +0 -0
  65. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/reload.py +0 -0
  66. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/restart.py +0 -0
  67. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/setup.py +0 -0
  68. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/sh.py +0 -0
  69. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/show.py +0 -0
  70. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/sql.py +0 -0
  71. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/stack.py +0 -0
  72. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/stop.py +0 -0
  73. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/tag.py +0 -0
  74. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/uninstall.py +0 -0
  75. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/upgrade.py +0 -0
  76. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/actions/verify.py +0 -0
  77. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/_chain.py +0 -0
  78. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/_events.py +0 -0
  79. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/_exceptions.py +0 -0
  80. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/_oauth2.py +0 -0
  81. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/_websockets.py +0 -0
  82. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/__init__.py +0 -0
  83. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/assets/__init__.py +0 -0
  84. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/assets/ansi_up.js +0 -0
  85. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/assets/banner_1920x320.png +0 -0
  86. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/assets/favicon.ico +0 -0
  87. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/assets/logo_48x48.png +0 -0
  88. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/assets/logo_500x500.png +0 -0
  89. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/callbacks/__init__.py +0 -0
  90. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/callbacks/custom.py +0 -0
  91. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/callbacks/dashboard.py +0 -0
  92. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/callbacks/jobs.py +0 -0
  93. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/callbacks/login.py +0 -0
  94. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/callbacks/pipes.py +0 -0
  95. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/callbacks/plugins.py +0 -0
  96. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/callbacks/register.py +0 -0
  97. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/components.py +0 -0
  98. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/connectors.py +0 -0
  99. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/graphs.py +0 -0
  100. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/jobs.py +0 -0
  101. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/keys.py +0 -0
  102. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/pages/__init__.py +0 -0
  103. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/pages/dashboard.py +0 -0
  104. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/pages/error.py +0 -0
  105. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/pages/job.py +0 -0
  106. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/pages/login.py +0 -0
  107. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/pages/pipes.py +0 -0
  108. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/pages/plugins.py +0 -0
  109. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/pages/register.py +0 -0
  110. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/pipes.py +0 -0
  111. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/plugins.py +0 -0
  112. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/sessions.py +0 -0
  113. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/sync.py +0 -0
  114. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/users.py +0 -0
  115. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/websockets.py +0 -0
  116. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/dash/webterm.py +0 -0
  117. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/models/__init__.py +0 -0
  118. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/models/_interfaces.py +0 -0
  119. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/models/_locations.py +0 -0
  120. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/models/_metrics.py +0 -0
  121. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/models/_pipes.py +0 -0
  122. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/__init__.py +0 -0
  123. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/__init__.py +0 -0
  124. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/css/__init__.py +0 -0
  125. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/css/bootstrap.min.css +0 -0
  126. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/css/dash.css +0 -0
  127. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/css/dbc_dark.css +0 -0
  128. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/css/styles.css +0 -0
  129. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/css/xterm.css +0 -0
  130. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/ico/__init__.py +0 -0
  131. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/ico/logo.ico +0 -0
  132. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/js/__init__.py +0 -0
  133. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/js/action_button.js +0 -0
  134. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/js/main.js +0 -0
  135. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/js/terminado.js +0 -0
  136. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/js/xterm.js +0 -0
  137. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/static/png/__init__.py +0 -0
  138. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/templates/__init__.py +0 -0
  139. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/templates/index.html +0 -0
  140. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/templates/old_index.html +0 -0
  141. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/templates/secret.html +0 -0
  142. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/resources/templates/termpage.html +0 -0
  143. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/routes/__init__.py +0 -0
  144. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/routes/_actions.py +0 -0
  145. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/routes/_connectors.py +0 -0
  146. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/routes/_index.py +0 -0
  147. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/routes/_jobs.py +0 -0
  148. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/routes/_login.py +0 -0
  149. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/routes/_misc.py +0 -0
  150. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/routes/_plugins.py +0 -0
  151. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/routes/_users.py +0 -0
  152. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/routes/_version.py +0 -0
  153. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/routes/_webterm.py +0 -0
  154. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/api/tables/__init__.py +0 -0
  155. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/__init__.py +0 -0
  156. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/_dash.py +0 -0
  157. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/_edit.py +0 -0
  158. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/_environment.py +0 -0
  159. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/_formatting.py +0 -0
  160. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/_jobs.py +0 -0
  161. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/_patch.py +0 -0
  162. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/_paths.py +0 -0
  163. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/_preprocess.py +0 -0
  164. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/_read_config.py +0 -0
  165. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/_shell.py +0 -0
  166. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/_sync.py +0 -0
  167. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/paths.py +0 -0
  168. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/resources/__init__.py +0 -0
  169. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/stack/__init__.py +0 -0
  170. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/stack/grafana/__init__.py +0 -0
  171. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/stack/mosquitto/__init__.py +0 -0
  172. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/stack/mosquitto/resources/__init__.py +0 -0
  173. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/config/stack/resources/__init__.py +0 -0
  174. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/_Connector.py +0 -0
  175. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/__init__.py +0 -0
  176. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/api/_APIConnector.py +0 -0
  177. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/api/__init__.py +0 -0
  178. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/api/_actions.py +0 -0
  179. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/api/_fetch.py +0 -0
  180. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/api/_jobs.py +0 -0
  181. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/api/_login.py +0 -0
  182. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/api/_misc.py +0 -0
  183. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/api/_pipes.py +0 -0
  184. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/api/_plugins.py +0 -0
  185. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/api/_request.py +0 -0
  186. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/api/_uri.py +0 -0
  187. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/api/_users.py +0 -0
  188. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/parse.py +0 -0
  189. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/plugin/PluginConnector.py +0 -0
  190. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/plugin/__init__.py +0 -0
  191. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/poll.py +0 -0
  192. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/__init__.py +0 -0
  193. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/_fetch.py +0 -0
  194. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/_instance.py +0 -0
  195. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/_pipes.py +0 -0
  196. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/_plugins.py +0 -0
  197. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/_sql.py +0 -0
  198. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/_uri.py +0 -0
  199. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/tables/__init__.py +0 -0
  200. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/tables/types.py +0 -0
  201. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/sql/tools.py +0 -0
  202. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/valkey/_ValkeyConnector.py +0 -0
  203. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/valkey/__init__.py +0 -0
  204. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/valkey/_fetch.py +0 -0
  205. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/valkey/_pipes.py +0 -0
  206. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/valkey/_plugins.py +0 -0
  207. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/connectors/valkey/_users.py +0 -0
  208. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/__init__.py +0 -0
  209. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_attributes.py +0 -0
  210. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_bootstrap.py +0 -0
  211. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_clear.py +0 -0
  212. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_copy.py +0 -0
  213. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_data.py +0 -0
  214. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_deduplicate.py +0 -0
  215. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_delete.py +0 -0
  216. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_drop.py +0 -0
  217. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_dtypes.py +0 -0
  218. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_edit.py +0 -0
  219. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_fetch.py +0 -0
  220. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_index.py +0 -0
  221. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_register.py +0 -0
  222. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_show.py +0 -0
  223. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Pipe/_sync.py +0 -0
  224. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/Plugin/__init__.py +0 -0
  225. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/User/_User.py +0 -0
  226. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/User/__init__.py +0 -0
  227. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/core/__init__.py +0 -0
  228. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/jobs/_Executor.py +0 -0
  229. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/jobs/_Job.py +0 -0
  230. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/jobs/__init__.py +0 -0
  231. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/plugins/_Plugin.py +0 -0
  232. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/plugins/__init__.py +0 -0
  233. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/plugins/bootstrap.py +0 -0
  234. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/__init__.py +0 -0
  235. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/_get_pipes.py +0 -0
  236. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/daemon/Daemon.py +0 -0
  237. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -0
  238. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/daemon/RotatingFile.py +0 -0
  239. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/daemon/StdinFile.py +0 -0
  240. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/daemon/__init__.py +0 -0
  241. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/daemon/_names.py +0 -0
  242. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/debug.py +0 -0
  243. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/dtypes/__init__.py +0 -0
  244. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/dtypes/sql.py +0 -0
  245. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/formatting/__init__.py +0 -0
  246. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/formatting/_jobs.py +0 -0
  247. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/formatting/_pipes.py +0 -0
  248. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/formatting/_pprint.py +0 -0
  249. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/formatting/_shell.py +0 -0
  250. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/interactive.py +0 -0
  251. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/misc.py +0 -0
  252. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/networking.py +0 -0
  253. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/packages/__init__.py +0 -0
  254. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/packages/lazy_loader.py +0 -0
  255. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/pool.py +0 -0
  256. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/prompt.py +0 -0
  257. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/threading.py +0 -0
  258. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/typing.py +0 -0
  259. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/venv/_Venv.py +0 -0
  260. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/warnings.py +0 -0
  261. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum/utils/yaml.py +0 -0
  262. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum.egg-info/SOURCES.txt +0 -0
  263. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum.egg-info/dependency_links.txt +0 -0
  264. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum.egg-info/entry_points.txt +0 -0
  265. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum.egg-info/requires.txt +0 -0
  266. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum.egg-info/top_level.txt +0 -0
  267. {meerschaum-2.8.2 → meerschaum-2.8.4}/meerschaum.egg-info/zip-safe +0 -0
  268. {meerschaum-2.8.2 → meerschaum-2.8.4}/setup.cfg +0 -0
  269. {meerschaum-2.8.2 → meerschaum-2.8.4}/setup.py +0 -0
  270. {meerschaum-2.8.2 → meerschaum-2.8.4}/tests/test_actions.py +0 -0
  271. {meerschaum-2.8.2 → meerschaum-2.8.4}/tests/test_arguments.py +0 -0
  272. {meerschaum-2.8.2 → meerschaum-2.8.4}/tests/test_deduplicate.py +0 -0
  273. {meerschaum-2.8.2 → meerschaum-2.8.4}/tests/test_jobs.py +0 -0
  274. {meerschaum-2.8.2 → meerschaum-2.8.4}/tests/test_pipe_data.py +0 -0
  275. {meerschaum-2.8.2 → meerschaum-2.8.4}/tests/test_pipes_dtypes.py +0 -0
  276. {meerschaum-2.8.2 → meerschaum-2.8.4}/tests/test_sql.py +0 -0
  277. {meerschaum-2.8.2 → meerschaum-2.8.4}/tests/test_sync.py +0 -0
  278. {meerschaum-2.8.2 → meerschaum-2.8.4}/tests/test_users.py +0 -0
  279. {meerschaum-2.8.2 → meerschaum-2.8.4}/tests/test_verify.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: meerschaum
3
- Version: 2.8.2
3
+ Version: 2.8.4
4
4
  Summary: Sync Time-Series Pipes with Meerschaum
5
5
  Home-page: https://meerschaum.io
6
6
  Author: Bennett Meares
@@ -367,6 +367,11 @@ groups['sync'].add_argument(
367
367
  "Only compare row-counts when verifying pipes."
368
368
  ),
369
369
  )
370
+ groups['sync'].add_argument(
371
+ '--skip-hooks', action='store_true', help=(
372
+ "Skip calling the sync hooks."
373
+ )
374
+ )
370
375
  groups['sync'].add_argument(
371
376
  '--cache', action='store_true',
372
377
  help=(
@@ -86,7 +86,7 @@ def _drop_pipes(
86
86
  pprint(success_dict)
87
87
 
88
88
  msg = (
89
- f"Finished dropping {len(pipes)} pipes"
89
+ f"Finished dropping {len(pipes)} pipe"
90
90
  + ('s' if len(pipes) != 1 else '')
91
91
  + f"\n ({successes} succeeded, {fails} failed)."
92
92
  )
@@ -568,9 +568,9 @@ def _start_pipeline(
568
568
  Examples
569
569
  --------
570
570
 
571
- `sync pipes -i sql:local + sync pipes -i sql:main :: -s 'daily'`
571
+ `sync pipes -i sql:local + sync pipes -i sql:main : -s 'daily'`
572
572
 
573
- `show version + show arguments :: --loop`
573
+ `show version + show arguments : --loop`
574
574
 
575
575
  """
576
576
  import json
@@ -580,10 +580,11 @@ def _start_pipeline(
580
580
  from meerschaum.utils.warnings import info, warn
581
581
  from meerschaum.utils.misc import is_int
582
582
  from meerschaum.utils.venv import venv_exec
583
- from meerschaum.utils.process import poll_process
583
+ from meerschaum.utils.process import poll_process, _stop_process
584
584
  fence_begin, fence_end = '<MRSM_RESULT>', '</MRSM_RESULT>'
585
585
 
586
- success, msg = False, "Did not run pipeline."
586
+ default_msg = "Did not pipeline."
587
+ success, msg = False, default_msg
587
588
  def write_line(line):
588
589
  nonlocal success, msg
589
590
  decoded = line.decode('utf-8')
@@ -610,7 +611,9 @@ def _start_pipeline(
610
611
  do_n_times = (
611
612
  int(action[0].lstrip('x'))
612
613
  if action and is_int(action[0].lstrip('x'))
613
- else 1
614
+ else (
615
+ 1 if not loop else -1
616
+ )
614
617
  )
615
618
 
616
619
  params = params or {}
@@ -623,8 +626,9 @@ def _start_pipeline(
623
626
  if min_seconds is None:
624
627
  min_seconds = 1.0
625
628
 
629
+ proc = None
626
630
  def do_entry() -> None:
627
- nonlocal success, msg
631
+ nonlocal success, msg, proc
628
632
  if timeout_seconds is None:
629
633
  success, msg = entry(sub_args_line, _patch_args=patch_args)
630
634
  return
@@ -674,6 +678,10 @@ def _start_pipeline(
674
678
  run_loop()
675
679
  except KeyboardInterrupt:
676
680
  warn("Cancelled pipeline.", stack=False)
681
+ if proc is not None:
682
+ _stop_process(proc)
683
+ if msg == default_msg:
684
+ msg = "Pipeline was cancelled."
677
685
 
678
686
  if do_n_times != 1:
679
687
  info(f"Ran pipeline {ran_n_times} time" + ('s' if ran_n_times != 1 else '') + '.')
@@ -50,6 +50,7 @@ def _pipes_lap(
50
50
  bounded: Optional[bool] = None,
51
51
  chunk_interval: Union[timedelta, int, None] = None,
52
52
  check_rowcounts_only: bool = False,
53
+ skip_hooks: bool = False,
53
54
  mrsm_instance: Optional[str] = None,
54
55
  timeout_seconds: Optional[int] = None,
55
56
  nopretty: bool = False,
@@ -95,6 +96,7 @@ def _pipes_lap(
95
96
  'bounded': bounded,
96
97
  'chunk_interval': chunk_interval,
97
98
  'check_rowcounts_only': check_rowcounts_only,
99
+ 'skip_hooks': skip_hooks,
98
100
  })
99
101
  locks = {'remaining_count': Lock(), 'results_dict': Lock(), 'pipes_threads': Lock(),}
100
102
  pipes = get_pipes(
@@ -257,6 +259,7 @@ def _sync_pipes(
257
259
  bounded: Optional[bool] = None,
258
260
  chunk_interval: Union[timedelta, int, None] = None,
259
261
  check_rowcounts_only: bool = False,
262
+ skip_hooks: bool = False,
260
263
  shell: bool = False,
261
264
  nopretty: bool = False,
262
265
  debug: bool = False,
@@ -289,6 +292,8 @@ def _sync_pipes(
289
292
 
290
293
  noninteractive_val = os.environ.get(STATIC_CONFIG['environment']['noninteractive'], None)
291
294
  noninteractive = str(noninteractive_val).lower() in ('1', 'true', 'yes')
295
+ if check_rowcounts_only:
296
+ skip_hooks = True
292
297
 
293
298
  run = True
294
299
  msg = ""
@@ -312,6 +317,7 @@ def _sync_pipes(
312
317
  bounded=bounded,
313
318
  chunk_interval=chunk_interval,
314
319
  check_rowcounts_only=check_rowcounts_only,
320
+ skip_hooks=skip_hooks,
315
321
  unblock=unblock,
316
322
  debug=debug,
317
323
  nopretty=nopretty,
@@ -403,6 +409,7 @@ def _wrap_pipe(
403
409
  deduplicate: bool = False,
404
410
  bounded: Optional[bool] = None,
405
411
  chunk_interval: Union[timedelta, int, None] = None,
412
+ skip_hooks: bool = False,
406
413
  **kw
407
414
  ):
408
415
  """
@@ -462,6 +469,8 @@ def _wrap_pipe(
462
469
 
463
470
  pre_hook_results, post_hook_results = [], []
464
471
  def apply_hooks(is_pre_sync: bool):
472
+ if skip_hooks:
473
+ return
465
474
  _sync_hooks = (_pre_sync_hooks if is_pre_sync else _post_sync_hooks)
466
475
  _hook_results = (pre_hook_results if is_pre_sync else post_hook_results)
467
476
  for module_name, sync_hooks in _sync_hooks.items():
@@ -9,6 +9,7 @@ from __future__ import annotations
9
9
 
10
10
  import os
11
11
  from collections import defaultdict
12
+ from fnmatch import fnmatch
12
13
 
13
14
  import meerschaum as mrsm
14
15
  from meerschaum.utils.typing import Dict, Any, Optional, PipesDict
@@ -21,6 +22,7 @@ from meerschaum.plugins import _api_plugins
21
22
  from meerschaum.utils.warnings import warn, dprint
22
23
  from meerschaum.utils.threading import RLock
23
24
  from meerschaum.utils.misc import is_pipe_registered
25
+ from meerschaum.connectors.parse import parse_instance_keys
24
26
 
25
27
  from meerschaum import __version__ as version
26
28
  __version__ = version
@@ -99,7 +101,6 @@ production = get_uvicorn_config().get('production', False)
99
101
  _include_dash = (not no_dash)
100
102
  docs_enabled = not production or sys_config.get('endpoints', {}).get('docs_in_production', True)
101
103
 
102
- connector = None
103
104
  default_instance_keys = None
104
105
  _instance_connectors = defaultdict(lambda: None)
105
106
  def get_api_connector(instance_keys: Optional[str] = None):
@@ -119,23 +120,35 @@ def get_api_connector(instance_keys: Optional[str] = None):
119
120
  )
120
121
 
121
122
  allowed_instance_keys = permissions_config.get(
122
- 'instance', {}
123
+ 'instances', {}
123
124
  ).get(
124
125
  'allowed_instance_keys',
125
126
  ['*']
126
127
  )
127
- if allowed_instance_keys != ['*'] and instance_keys not in allowed_instance_keys:
128
+ found_match: bool = False
129
+ for allowed_keys_pattern in allowed_instance_keys:
130
+ if fnmatch(instance_keys, allowed_keys_pattern):
131
+ found_match = True
132
+ break
133
+ if not found_match:
128
134
  raise APIPermissionError(
129
- f"Instance keys '{instance_keys}' not in list of allowed instances."
135
+ f"Instance keys '{instance_keys}' does not match the allowed instances patterns."
130
136
  )
131
137
 
132
138
  with _locks[f'instance-{instance_keys}']:
133
- connector = _instance_connectors[instance_keys]
134
- if connector is None:
135
- from meerschaum.connectors.parse import parse_instance_keys
136
- connector = parse_instance_keys(instance_keys, debug=debug)
137
- _instance_connectors[instance_keys] = connector
138
- return connector
139
+ if _instance_connectors[instance_keys] is None:
140
+ try:
141
+ is_valid_connector = True
142
+ _instance_connectors[instance_keys] = parse_instance_keys(instance_keys, debug=debug)
143
+ except Exception:
144
+ is_valid_connector = False
145
+
146
+ if not is_valid_connector:
147
+ raise fastapi.HTTPException(
148
+ status_code=422,
149
+ detail="Invalid instance keys.",
150
+ )
151
+ return _instance_connectors[instance_keys]
139
152
 
140
153
 
141
154
  cache_connector = None
@@ -164,7 +177,6 @@ def get_cache_connector(connector_keys: Optional[str] = None):
164
177
  return None
165
178
 
166
179
  if cache_connector is None:
167
- from meerschaum.connectors.parse import parse_instance_keys
168
180
  cache_connector = parse_instance_keys(connector_keys)
169
181
 
170
182
  if debug:
@@ -272,7 +272,7 @@ async def get_pipes_by_connector_and_metric(
272
272
 
273
273
 
274
274
  @app.get(pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}', tags=['Pipes'])
275
- async def get_pipes_by_connector_and_metric_and_location(
275
+ async def get_pipe_by_connector_and_metric_and_location(
276
276
  connector_keys: str,
277
277
  metric_key: str,
278
278
  location_key: str,
@@ -299,7 +299,7 @@ async def get_pipes_by_connector_and_metric_and_location(
299
299
  detail=f"location_key '{location_key}' not found."
300
300
  )
301
301
 
302
- return str(pipes(instance_keys)[connector_keys][metric_key][location_key])
302
+ return pipes(instance_keys)[connector_keys][metric_key][location_key].attributes
303
303
 
304
304
 
305
305
  @app.get(pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/sync_time', tags=['Pipes'])
@@ -311,7 +311,7 @@ def get_sync_time(
311
311
  newest: bool = True,
312
312
  remote: bool = False,
313
313
  round_down: bool = True,
314
- debug: bool = False,
314
+ instance_keys: Optional[str] = None,
315
315
  curr_user = (
316
316
  fastapi.Depends(manager) if not no_auth else None
317
317
  ),
@@ -322,12 +322,11 @@ def get_sync_time(
322
322
  """
323
323
  if location_key == '[None]':
324
324
  location_key = None
325
- pipe = get_pipe(connector_keys, metric_key, location_key)
325
+ pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
326
326
  sync_time = pipe.get_sync_time(
327
- params = params,
328
- newest = newest,
329
- debug = debug,
330
- round_down = round_down,
327
+ params=params,
328
+ newest=newest,
329
+ round_down=round_down,
331
330
  )
332
331
  if isinstance(sync_time, datetime):
333
332
  sync_time = sync_time.isoformat()
@@ -339,7 +338,8 @@ def sync_pipe(
339
338
  connector_keys: str,
340
339
  metric_key: str,
341
340
  location_key: str,
342
- data: dict = None,
341
+ data: Union[List[Dict[Any, Any]], Dict[Any, Any]],
342
+ instance_keys: Optional[str] = None,
343
343
  check_existing: bool = True,
344
344
  blocking: bool = True,
345
345
  force: bool = False,
@@ -354,9 +354,9 @@ def sync_pipe(
354
354
  Add data to an existing Pipe.
355
355
  See [`meerschaum.Pipe.sync`](https://docs.meerschaum.io/meerschaum.html#Pipe.sync).
356
356
  """
357
- if data is None:
358
- data = {}
359
- pipe = get_pipe(connector_keys, metric_key, location_key)
357
+ if not data:
358
+ return [True, "No data to sync."]
359
+ pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
360
360
  if pipe.target in ('mrsm_users', 'mrsm_plugins', 'mrsm_pipes'):
361
361
  raise fastapi.HTTPException(
362
362
  status_code=409,
@@ -365,21 +365,16 @@ def sync_pipe(
365
365
 
366
366
  if not pipe.columns and columns is not None:
367
367
  pipe.columns = json.loads(columns)
368
- if not pipe.columns and not is_pipe_registered(pipe, pipes(refresh=True)):
369
- raise fastapi.HTTPException(
370
- status_code=409,
371
- detail="Pipe must be registered with index columns specified."
372
- )
373
368
 
374
- result = list(pipe.sync(
369
+ success, msg = pipe.sync(
375
370
  data,
376
371
  debug=debug,
377
372
  check_existing=check_existing,
378
373
  blocking=blocking,
379
374
  force=force,
380
375
  workers=workers,
381
- ))
382
- return result
376
+ )
377
+ return list((success, msg))
383
378
 
384
379
 
385
380
  @app.get(pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/data', tags=['Pipes'])
@@ -527,7 +522,6 @@ def get_pipe_csv(
527
522
  if params == 'null':
528
523
  params = None
529
524
  if params is not None:
530
- import json
531
525
  try:
532
526
  _params = json.loads(params)
533
527
  except Exception:
@@ -543,7 +537,7 @@ def get_pipe_csv(
543
537
  if not is_pipe_registered(pipe, pipes(instance_keys, refresh=True)):
544
538
  raise fastapi.HTTPException(
545
539
  status_code=409,
546
- detail="Pipe must be registered with the datetime column specified."
540
+ detail="Pipe must be registered."
547
541
  )
548
542
 
549
543
  dt_col = pipe.columns.get('datetime', None)
@@ -173,6 +173,9 @@ default_pipes_config = {
173
173
  'sync': {
174
174
  'filter_params_index_limit': 250,
175
175
  },
176
+ 'verify': {
177
+ 'max_chunks_syncs': 3,
178
+ },
176
179
  }
177
180
  default_plugins_config = {}
178
181
  default_experimental_config = {
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "2.8.2"
5
+ __version__ = "2.8.4"
@@ -137,7 +137,7 @@ STATIC_CONFIG: Dict[str, Any] = {
137
137
  'pbkdf2_sha256__default_rounds': 1_000_000,
138
138
  },
139
139
  'min_username_length': 1,
140
- 'max_username_length': 26,
140
+ 'max_username_length': 60,
141
141
  'min_password_length': 5,
142
142
  },
143
143
  'plugins': {
@@ -184,7 +184,7 @@ class SQLConnector(Connector):
184
184
  ### ensure flavor and label are set accordingly
185
185
  if 'flavor' not in self.__dict__:
186
186
  if flavor is None and 'uri' not in self.__dict__:
187
- raise Exception(
187
+ raise ValueError(
188
188
  f" Missing flavor. Provide flavor as a key for '{self}'."
189
189
  )
190
190
  self.flavor = flavor or self.parse_uri(self.__dict__['uri']).get('flavor', None)
@@ -100,7 +100,13 @@ def _cli_exit(
100
100
  ### yet defined (e.g. 'sql:local').
101
101
  cli_arg_str = self.DATABASE_URL
102
102
  if self.flavor in ('sqlite', 'duckdb'):
103
- cli_arg_str = str(self.database)
103
+ cli_arg_str = (
104
+ str(self.database)
105
+ if 'database' in self.__dict__
106
+ else self.parse_uri(self.URI).get('database', None)
107
+ )
108
+ if not cli_arg_str:
109
+ raise ValueError(f"Cannot determine database from connector '{self}'.")
104
110
  if cli_arg_str.startswith('postgresql+psycopg://'):
105
111
  cli_arg_str = cli_arg_str.replace('postgresql+psycopg://', 'postgresql://')
106
112
 
@@ -256,8 +256,11 @@ def create_engine(
256
256
  ) if not _uri else _uri
257
257
 
258
258
  ### Sometimes the timescaledb:// flavor can slip in.
259
- if _uri and self.flavor in ('timescaledb',) and self.flavor in _uri:
260
- engine_str = engine_str.replace(f'{self.flavor}', 'postgresql', 1)
259
+ if _uri and self.flavor in _uri:
260
+ if self.flavor == 'timescaledb':
261
+ engine_str = engine_str.replace(f'{self.flavor}', 'postgresql', 1)
262
+ elif _uri.startswith('postgresql://'):
263
+ engine_str = engine_str.replace('postgresql://', 'postgresql+psycopg2://')
261
264
 
262
265
  if debug:
263
266
  dprint(
@@ -303,7 +306,7 @@ def create_engine(
303
306
  echo = debug,
304
307
  **_create_engine_args
305
308
  )
306
- except Exception as e:
309
+ except Exception:
307
310
  warn(f"Failed to create connector '{self}':\n{traceback.format_exc()}", stack=False)
308
311
  engine = None
309
312
 
@@ -43,7 +43,9 @@ def register_user(
43
43
  'password_hash': user.password_hash,
44
44
  'user_type': user.type,
45
45
  'attributes': (
46
- json.dumps(user.attributes) if self.flavor not in json_flavors else user.attributes
46
+ json.dumps(user.attributes)
47
+ if self.flavor not in json_flavors
48
+ else user.attributes
47
49
  ),
48
50
  }
49
51
  if old_id is not None:
@@ -109,7 +111,7 @@ def edit_user(
109
111
  user_id = user.user_id if user.user_id is not None else self.get_user_id(user, debug=debug)
110
112
  if user_id is None:
111
113
  return False, (
112
- f"User '{user.username}' does not exist. " +
114
+ f"User '{user.username}' does not exist. "
113
115
  f"Register user '{user.username}' before editing."
114
116
  )
115
117
  user.user_id = user_id
@@ -7,6 +7,7 @@ Verify the contents of a pipe by resyncing its interval.
7
7
  """
8
8
 
9
9
  from datetime import datetime, timedelta
10
+ import time
10
11
 
11
12
  import meerschaum as mrsm
12
13
  from meerschaum.utils.typing import SuccessTuple, Any, Optional, Union, Tuple, Dict
@@ -118,7 +119,7 @@ def verify(
118
119
  begin, end = self.parse_date_bounds(begin, end)
119
120
  cannot_determine_bounds = bounded and begin is None and end is None
120
121
 
121
- if cannot_determine_bounds:
122
+ if cannot_determine_bounds and not check_rowcounts_only:
122
123
  warn(f"Cannot determine sync bounds for {self}. Syncing instead...", stack=False)
123
124
  sync_success, sync_msg = self.sync(
124
125
  begin=begin,
@@ -183,9 +184,12 @@ def verify(
183
184
  )
184
185
  )
185
186
  message_header = f"{begin_to_print} - {end_to_print}"
187
+ max_chunks_syncs = mrsm.get_config('pipes', 'verify', 'max_chunks_syncs')
186
188
 
187
189
  info(
188
- f"Verifying {self}:\n Syncing {len(chunk_bounds)} chunk"
190
+ f"Verifying {self}:\n "
191
+ + ("Syncing" if not check_rowcounts_only else "Checking")
192
+ + f" {len(chunk_bounds)} chunk"
189
193
  + ('s' if len(chunk_bounds) != 1 else '')
190
194
  + f" ({'un' if not bounded else ''}bounded)"
191
195
  + f" of size '{interval_str(chunk_interval)}'"
@@ -219,9 +223,9 @@ def verify(
219
223
  debug=debug,
220
224
  )
221
225
  checked_rows_str = (
222
- f"checked {existing_rowcount} row"
226
+ f"checked {existing_rowcount:,} row"
223
227
  + ("s" if existing_rowcount != 1 else '')
224
- + f" vs {remote_rowcount} remote"
228
+ + f" vs {remote_rowcount:,} remote"
225
229
  )
226
230
  if (
227
231
  existing_rowcount is not None
@@ -239,19 +243,26 @@ def verify(
239
243
  f"Row-counts are out-of-sync ({checked_rows_str})."
240
244
  )
241
245
 
242
- chunk_success, chunk_msg = self.sync(
243
- begin=chunk_begin,
244
- end=chunk_end,
245
- params=params,
246
- workers=_workers,
247
- debug=debug,
248
- **kwargs
249
- ) if do_sync else (chunk_success, chunk_msg)
246
+ num_syncs = 0
247
+ while num_syncs < max_chunks_syncs:
248
+ chunk_success, chunk_msg = self.sync(
249
+ begin=chunk_begin,
250
+ end=chunk_end,
251
+ params=params,
252
+ workers=_workers,
253
+ debug=debug,
254
+ **kwargs
255
+ ) if do_sync else (chunk_success, chunk_msg)
256
+ if chunk_success:
257
+ break
258
+ num_syncs += 1
259
+ time.sleep(num_syncs**2)
250
260
  chunk_msg = chunk_msg.strip()
251
261
  if ' - ' not in chunk_msg:
252
262
  chunk_label = f"{chunk_begin} - {chunk_end}"
253
263
  chunk_msg = f'Verified chunk for {self}:\n{chunk_label}\n{chunk_msg}'
254
264
  mrsm.pprint((chunk_success, chunk_msg))
265
+
255
266
  return chunk_begin_and_end, (chunk_success, chunk_msg)
256
267
 
257
268
  ### If we have more than one chunk, attempt to sync the first one and return if its fails.
@@ -284,6 +295,17 @@ def verify(
284
295
  _batch_begin = batch_chunk_bounds[0][0]
285
296
  _batch_end = batch_chunk_bounds[-1][-1]
286
297
  batch_message_header = f"{_batch_begin} - {_batch_end}"
298
+
299
+ if check_rowcounts_only:
300
+ info(f"Checking row-counts for batch bounds:\n {batch_message_header}")
301
+ _, (batch_init_success, batch_init_msg) = process_chunk_bounds(
302
+ (_batch_begin, _batch_end)
303
+ )
304
+ mrsm.pprint((batch_init_success, batch_init_msg))
305
+ if batch_init_success and 'up-to-date' in batch_init_msg:
306
+ info("Entire batch is up-to-date.")
307
+ return batch_init_success, batch_init_msg
308
+
287
309
  batch_bounds_success_tuples = dict(pool.map(process_chunk_bounds, batch_chunk_bounds))
288
310
  bounds_success_tuples.update(batch_bounds_success_tuples)
289
311
  batch_bounds_success_bools = {
@@ -395,7 +417,7 @@ def verify(
395
417
  retry_failed_batch = False
396
418
 
397
419
  batch_msg_to_print = (
398
- f"{make_header('Completed batch ' + batch_counter_str + ' ' + for_self + ':')}\n{batch_msg}"
420
+ f"{make_header('Completed batch ' + batch_counter_str + ':')}\n{batch_msg}"
399
421
  )
400
422
  mrsm.pprint((batch_success, batch_msg_to_print))
401
423
 
@@ -471,11 +493,13 @@ def get_chunks_success_message(
471
493
  header = (header + "\n") if header else ""
472
494
  stats_msg = items_str(
473
495
  (
474
- ([f'inserted {num_inserted:,}'] if num_inserted else [])
475
- + ([f'updated {num_updated:,}'] if num_updated else [])
476
- + ([f'upserted {num_upserted:,}'] if num_upserted else [])
477
- + ([f'checked {num_checked:,}'] if num_checked else [])
478
- ) or ['synced 0'],
496
+ (
497
+ ([f'inserted {num_inserted:,}'] if num_inserted else [])
498
+ + ([f'updated {num_updated:,}'] if num_updated else [])
499
+ + ([f'upserted {num_upserted:,}'] if num_upserted else [])
500
+ + ([f'checked {num_checked:,}'] if num_checked else [])
501
+ ) or ['synced 0']
502
+ ),
479
503
  quotes=False,
480
504
  and_=False,
481
505
  )
@@ -154,7 +154,7 @@ class SystemdExecutor(Executor):
154
154
  STATIC_CONFIG['environment']['systemd_delete_job']: (
155
155
  '1'
156
156
  if job.delete_after_completion
157
- else '0',
157
+ else '0'
158
158
  ),
159
159
  })
160
160
 
@@ -222,7 +222,7 @@ def filter_unseen_df(
222
222
  numeric_cols_precisions_scales = {
223
223
  col: get_numeric_precision_scale(None, typ)
224
224
  for col, typ in dtypes.items()
225
- if col and typ and typ.startswith('numeric')
225
+ if col and str(typ).lower().startswith('numeric')
226
226
  }
227
227
 
228
228
  dt_dtypes = {
@@ -58,7 +58,7 @@ packages: Dict[str, Dict[str, str]] = {
58
58
  '_internal' : {
59
59
  'apscheduler' : (
60
60
  f"{_MRSM_PACKAGE_ARCHIVES_PREFIX}"
61
- "APScheduler-4.0.0a5.post75+mrsm-py3-none-any.whl>=4.0.0a5"
61
+ "APScheduler-4.0.0a5.post87+mrsm-py3-none-any.whl>=4.0.0a5"
62
62
  ),
63
63
  'dataclass_wizard' : 'dataclass-wizard>=0.28.0',
64
64
  },
@@ -9,15 +9,25 @@ See `meerschaum.utils.pool` for multiprocessing and
9
9
  """
10
10
 
11
11
  from __future__ import annotations
12
- import os, signal, subprocess, sys, platform, traceback
12
+
13
+ import os
14
+ import signal
15
+ import subprocess
16
+ import sys
17
+ import platform
18
+
19
+ import meerschaum as mrsm
13
20
  from meerschaum.utils.typing import Union, Optional, Any, Callable, Dict, Tuple
14
21
  from meerschaum.config.static import STATIC_CONFIG
15
22
 
16
23
  _child_processes = []
17
24
  def signal_handler(sig, frame):
18
25
  for child in _child_processes:
19
- child.send_signal(sig)
20
- child.wait()
26
+ try:
27
+ child.send_signal(sig)
28
+ child.wait()
29
+ except Exception:
30
+ pass
21
31
 
22
32
  def run_process(
23
33
  *args,
@@ -223,3 +233,17 @@ def poll_process(
223
233
  watchdog_thread.cancel()
224
234
 
225
235
  return proc.poll()
236
+
237
+
238
+ def _stop_process(
239
+ proc: subprocess.Popen,
240
+ timeout_seconds: int = 8,
241
+ ):
242
+ """
243
+ Stop a `subproccess.Popen` object.
244
+ """
245
+ proc.terminate()
246
+ try:
247
+ proc.wait(timeout=timeout_seconds)
248
+ except subprocess.TimeoutExpired:
249
+ proc.kill()
@@ -95,7 +95,7 @@ def schedule_function(
95
95
  A `SuccessTuple` upon exit.
96
96
  """
97
97
  import asyncio
98
- from meerschaum.utils.misc import filter_keywords, round_time
98
+ from meerschaum.utils.misc import filter_keywords
99
99
 
100
100
  global _scheduler
101
101
  kw['debug'] = debug
@@ -103,7 +103,7 @@ def schedule_function(
103
103
 
104
104
  _ = mrsm.attempt_import('attrs', lazy=False)
105
105
  apscheduler = mrsm.attempt_import('apscheduler', lazy=False)
106
- now = round_time(datetime.now(timezone.utc), timedelta(minutes=1))
106
+ now = datetime.now(timezone.utc)
107
107
  trigger = parse_schedule(schedule, now=now)
108
108
  _scheduler = apscheduler.AsyncScheduler(identity='mrsm-scheduler')
109
109
  try:
@@ -296,7 +296,7 @@ def parse_start_time(schedule: str, now: Optional[datetime] = None) -> datetime:
296
296
  dateutil_parser = mrsm.attempt_import('dateutil.parser')
297
297
  starting_parts = schedule.split(STARTING_KEYWORD)
298
298
  starting_str = ('now' if len(starting_parts) == 1 else starting_parts[-1]).strip()
299
- now = now or round_time(datetime.now(timezone.utc), timedelta(minutes=1))
299
+ now = now or datetime.now(timezone.utc)
300
300
  try:
301
301
  if starting_str == 'now':
302
302
  starting_ts = now
@@ -1548,7 +1548,7 @@ def get_update_queries(
1548
1548
  from meerschaum.utils.debug import dprint
1549
1549
  from meerschaum.utils.dtypes import are_dtypes_equal
1550
1550
  from meerschaum.utils.dtypes.sql import DB_FLAVORS_CAST_DTYPES, get_pd_type_from_db_type
1551
- flavor = flavor or (connectable.flavor if isinstance(connectable, SQLConnector) else None)
1551
+ flavor = flavor or getattr(connectable, 'flavor', None)
1552
1552
  if not flavor:
1553
1553
  raise ValueError("Provide a flavor if using a SQLAlchemy session.")
1554
1554
  if (