meerschaum 2.1.5__tar.gz → 2.1.7__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 (247) hide show
  1. {meerschaum-2.1.5/meerschaum.egg-info → meerschaum-2.1.7}/PKG-INFO +1 -1
  2. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/shell/Shell.py +1 -6
  3. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/api.py +1 -1
  4. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/sync.py +49 -21
  5. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/_shell.py +0 -1
  6. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/_version.py +1 -1
  7. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/_fetch.py +8 -11
  8. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/_pipes.py +47 -9
  9. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/tables/__init__.py +0 -16
  10. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_data.py +5 -4
  11. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_dtypes.py +2 -1
  12. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_sync.py +26 -13
  13. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/dataframe.py +183 -8
  14. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/formatting/_pipes.py +44 -10
  15. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/misc.py +34 -2
  16. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/packages/__init__.py +4 -3
  17. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/sql.py +27 -7
  18. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/typing.py +11 -0
  19. {meerschaum-2.1.5 → meerschaum-2.1.7/meerschaum.egg-info}/PKG-INFO +1 -1
  20. {meerschaum-2.1.5 → meerschaum-2.1.7}/tests/test_sync.py +31 -0
  21. {meerschaum-2.1.5 → meerschaum-2.1.7}/LICENSE +0 -0
  22. {meerschaum-2.1.5 → meerschaum-2.1.7}/NOTICE +0 -0
  23. {meerschaum-2.1.5 → meerschaum-2.1.7}/README.md +0 -0
  24. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/__init__.py +0 -0
  25. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/__main__.py +0 -0
  26. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/__init__.py +0 -0
  27. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/arguments/__init__.py +0 -0
  28. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/arguments/_parse_arguments.py +0 -0
  29. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/arguments/_parser.py +0 -0
  30. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/docs/__init__.py +0 -0
  31. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/docs/index.py +0 -0
  32. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/entry.py +0 -0
  33. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/gui/__init__.py +0 -0
  34. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/gui/app/__init__.py +0 -0
  35. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/gui/app/_windows.py +0 -0
  36. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/gui/app/actions.py +0 -0
  37. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/gui/app/pipes.py +0 -0
  38. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/shell/ShellCompleter.py +0 -0
  39. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/shell/ValidAutoSuggest.py +0 -0
  40. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/shell/__init__.py +0 -0
  41. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/shell/resources/__init__.py +0 -0
  42. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/term/TermPageHandler.py +0 -0
  43. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/term/__init__.py +0 -0
  44. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/_internal/term/tools.py +0 -0
  45. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/__init__.py +0 -0
  46. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/bootstrap.py +0 -0
  47. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/clear.py +0 -0
  48. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/copy.py +0 -0
  49. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/deduplicate.py +0 -0
  50. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/delete.py +0 -0
  51. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/drop.py +0 -0
  52. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/edit.py +0 -0
  53. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/install.py +0 -0
  54. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/login.py +0 -0
  55. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/os.py +0 -0
  56. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/pause.py +0 -0
  57. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/python.py +0 -0
  58. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/register.py +0 -0
  59. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/reload.py +0 -0
  60. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/setup.py +0 -0
  61. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/sh.py +0 -0
  62. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/show.py +0 -0
  63. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/sql.py +0 -0
  64. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/stack.py +0 -0
  65. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/start.py +0 -0
  66. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/stop.py +0 -0
  67. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/tag.py +0 -0
  68. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/uninstall.py +0 -0
  69. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/upgrade.py +0 -0
  70. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/actions/verify.py +0 -0
  71. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/__init__.py +0 -0
  72. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/_chain.py +0 -0
  73. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/_events.py +0 -0
  74. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/_oauth2.py +0 -0
  75. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/_websockets.py +0 -0
  76. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/__init__.py +0 -0
  77. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/actions.py +0 -0
  78. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/assets/__init__.py +0 -0
  79. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/assets/ansi_up.js +0 -0
  80. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/assets/banner_1920x320.png +0 -0
  81. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/assets/favicon.ico +0 -0
  82. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/assets/logo_48x48.png +0 -0
  83. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/assets/logo_500x500.png +0 -0
  84. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/callbacks/__init__.py +0 -0
  85. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/callbacks/dashboard.py +0 -0
  86. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/callbacks/jobs.py +0 -0
  87. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/callbacks/login.py +0 -0
  88. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/callbacks/plugins.py +0 -0
  89. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/callbacks/register.py +0 -0
  90. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/components.py +0 -0
  91. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/connectors.py +0 -0
  92. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/graphs.py +0 -0
  93. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/jobs.py +0 -0
  94. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/keys.py +0 -0
  95. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/pages/__init__.py +0 -0
  96. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/pages/dashboard.py +0 -0
  97. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/pages/error.py +0 -0
  98. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/pages/login.py +0 -0
  99. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/pages/plugins.py +0 -0
  100. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/pages/register.py +0 -0
  101. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/pipes.py +0 -0
  102. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/plugins.py +0 -0
  103. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/sync.py +0 -0
  104. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/users.py +0 -0
  105. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/websockets.py +0 -0
  106. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/dash/webterm.py +0 -0
  107. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/models/__init__.py +0 -0
  108. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/models/_interfaces.py +0 -0
  109. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/models/_locations.py +0 -0
  110. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/models/_metrics.py +0 -0
  111. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/models/_pipes.py +0 -0
  112. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/__init__.py +0 -0
  113. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/__init__.py +0 -0
  114. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/css/__init__.py +0 -0
  115. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/css/bootstrap.min.css +0 -0
  116. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/css/dash.css +0 -0
  117. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/css/dbc_dark.css +0 -0
  118. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/css/styles.css +0 -0
  119. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/css/xterm.css +0 -0
  120. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/ico/__init__.py +0 -0
  121. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/ico/logo.ico +0 -0
  122. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/js/__init__.py +0 -0
  123. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/js/action_button.js +0 -0
  124. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/js/main.js +0 -0
  125. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/js/terminado.js +0 -0
  126. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/js/xterm.js +0 -0
  127. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/static/png/__init__.py +0 -0
  128. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/templates/__init__.py +0 -0
  129. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/templates/index.html +0 -0
  130. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/templates/old_index.html +0 -0
  131. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/templates/secret.html +0 -0
  132. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/resources/templates/termpage.html +0 -0
  133. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/routes/__init__.py +0 -0
  134. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/routes/_actions.py +0 -0
  135. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/routes/_connectors.py +0 -0
  136. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/routes/_index.py +0 -0
  137. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/routes/_login.py +0 -0
  138. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/routes/_misc.py +0 -0
  139. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/routes/_pipes.py +0 -0
  140. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/routes/_plugins.py +0 -0
  141. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/routes/_users.py +0 -0
  142. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/routes/_version.py +0 -0
  143. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/routes/_webterm.py +0 -0
  144. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/api/tables/__init__.py +0 -0
  145. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/__init__.py +0 -0
  146. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/_default.py +0 -0
  147. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/_edit.py +0 -0
  148. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/_environment.py +0 -0
  149. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/_formatting.py +0 -0
  150. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/_jobs.py +0 -0
  151. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/_patch.py +0 -0
  152. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/_paths.py +0 -0
  153. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/_preprocess.py +0 -0
  154. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/_read_config.py +0 -0
  155. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/_sync.py +0 -0
  156. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/resources/__init__.py +0 -0
  157. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/stack/__init__.py +0 -0
  158. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/stack/grafana/__init__.py +0 -0
  159. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/stack/mosquitto/__init__.py +0 -0
  160. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/stack/mosquitto/resources/__init__.py +0 -0
  161. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/stack/resources/__init__.py +0 -0
  162. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/config/static/__init__.py +0 -0
  163. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/Connector.py +0 -0
  164. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/__init__.py +0 -0
  165. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/api/APIConnector.py +0 -0
  166. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/api/__init__.py +0 -0
  167. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/api/_actions.py +0 -0
  168. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/api/_fetch.py +0 -0
  169. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/api/_login.py +0 -0
  170. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/api/_misc.py +0 -0
  171. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/api/_pipes.py +0 -0
  172. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/api/_plugins.py +0 -0
  173. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/api/_request.py +0 -0
  174. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/api/_uri.py +0 -0
  175. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/api/_users.py +0 -0
  176. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/parse.py +0 -0
  177. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/plugin/PluginConnector.py +0 -0
  178. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/plugin/__init__.py +0 -0
  179. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/poll.py +0 -0
  180. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/SQLConnector.py +0 -0
  181. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/__init__.py +0 -0
  182. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/_cli.py +0 -0
  183. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/_create_engine.py +0 -0
  184. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/_instance.py +0 -0
  185. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/_plugins.py +0 -0
  186. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/_sql.py +0 -0
  187. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/_uri.py +0 -0
  188. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/_users.py +0 -0
  189. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/tables/types.py +0 -0
  190. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/connectors/sql/tools.py +0 -0
  191. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/__init__.py +0 -0
  192. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_attributes.py +0 -0
  193. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_bootstrap.py +0 -0
  194. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_clear.py +0 -0
  195. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_deduplicate.py +0 -0
  196. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_delete.py +0 -0
  197. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_drop.py +0 -0
  198. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_edit.py +0 -0
  199. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_fetch.py +0 -0
  200. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_register.py +0 -0
  201. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_show.py +0 -0
  202. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Pipe/_verify.py +0 -0
  203. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/Plugin/__init__.py +0 -0
  204. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/User/_User.py +0 -0
  205. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/User/__init__.py +0 -0
  206. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/core/__init__.py +0 -0
  207. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/plugins/_Plugin.py +0 -0
  208. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/plugins/__init__.py +0 -0
  209. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/__init__.py +0 -0
  210. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/_get_pipes.py +0 -0
  211. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/daemon/Daemon.py +0 -0
  212. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/daemon/RotatingFile.py +0 -0
  213. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/daemon/__init__.py +0 -0
  214. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/daemon/_names.py +0 -0
  215. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/debug.py +0 -0
  216. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/dtypes/__init__.py +0 -0
  217. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/dtypes/sql.py +0 -0
  218. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/formatting/__init__.py +0 -0
  219. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/formatting/_jobs.py +0 -0
  220. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/formatting/_pprint.py +0 -0
  221. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/formatting/_shell.py +0 -0
  222. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/interactive.py +0 -0
  223. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/networking.py +0 -0
  224. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/packages/_packages.py +0 -0
  225. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/packages/lazy_loader.py +0 -0
  226. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/pool.py +0 -0
  227. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/process.py +0 -0
  228. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/prompt.py +0 -0
  229. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/schedule.py +0 -0
  230. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/threading.py +0 -0
  231. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/venv/_Venv.py +0 -0
  232. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/venv/__init__.py +0 -0
  233. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/warnings.py +0 -0
  234. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum/utils/yaml.py +0 -0
  235. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum.egg-info/SOURCES.txt +0 -0
  236. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum.egg-info/dependency_links.txt +0 -0
  237. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum.egg-info/entry_points.txt +0 -0
  238. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum.egg-info/requires.txt +0 -0
  239. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum.egg-info/top_level.txt +0 -0
  240. {meerschaum-2.1.5 → meerschaum-2.1.7}/meerschaum.egg-info/zip-safe +0 -0
  241. {meerschaum-2.1.5 → meerschaum-2.1.7}/setup.cfg +0 -0
  242. {meerschaum-2.1.5 → meerschaum-2.1.7}/setup.py +0 -0
  243. {meerschaum-2.1.5 → meerschaum-2.1.7}/tests/test_deduplicate.py +0 -0
  244. {meerschaum-2.1.5 → meerschaum-2.1.7}/tests/test_pipes_dtypes.py +0 -0
  245. {meerschaum-2.1.5 → meerschaum-2.1.7}/tests/test_sql.py +0 -0
  246. {meerschaum-2.1.5 → meerschaum-2.1.7}/tests/test_users.py +0 -0
  247. {meerschaum-2.1.5 → meerschaum-2.1.7}/tests/test_verify.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: meerschaum
3
- Version: 2.1.5
3
+ Version: 2.1.7
4
4
  Summary: Sync Time-Series Pipes with Meerschaum
5
5
  Home-page: https://meerschaum.io
6
6
  Author: Bennett Meares
@@ -11,11 +11,7 @@ from copy import deepcopy
11
11
  from meerschaum.utils.typing import Union, SuccessTuple, Any, Callable, Optional, List, Dict
12
12
  from meerschaum.utils.packages import attempt_import
13
13
  from meerschaum.config import __doc__, __version__ as version, get_config
14
- cmd_import_name = get_config('shell', 'cmd')
15
- cmd_venv = None if cmd_import_name == 'cmd' else 'mrsm'
16
- cmd = attempt_import(cmd_import_name, venv=cmd_venv, warn=False, lazy=False)
17
- if cmd is None or isinstance(cmd, dict):
18
- cmd = attempt_import('cmd', lazy=False, warn=False)
14
+ import cmd
19
15
  _old_input = cmd.__builtins__['input']
20
16
  prompt_toolkit = attempt_import('prompt_toolkit', lazy=False, warn=False, install=True)
21
17
  (
@@ -53,7 +49,6 @@ hidden_commands = {
53
49
  'os',
54
50
  'sh',
55
51
  'pass',
56
- 'exit',
57
52
  'quit',
58
53
  'eof',
59
54
  'exit',
@@ -44,7 +44,7 @@ def api(
44
44
  sysargs = []
45
45
  if len(action) == 0:
46
46
  info(api.__doc__)
47
- return False, "Please provide a command to excecute (see above)."
47
+ return False, "Please provide a command to execute (see above)."
48
48
 
49
49
  boot_keywords = {'start', 'boot', 'init'}
50
50
  if action[0] in boot_keywords:
@@ -9,7 +9,7 @@ NOTE: `sync` required a SQL connection and is not intended for client use
9
9
  """
10
10
 
11
11
  from __future__ import annotations
12
- from datetime import timedelta
12
+ from datetime import timedelta, datetime, timezone
13
13
  import meerschaum as mrsm
14
14
  from meerschaum.utils.typing import SuccessTuple, Any, List, Optional, Tuple, Union
15
15
 
@@ -39,7 +39,7 @@ def sync(
39
39
 
40
40
  def _pipes_lap(
41
41
  workers: Optional[int] = None,
42
- debug: bool = None,
42
+ debug: Optional[bool] = None,
43
43
  unblock: bool = False,
44
44
  force: bool = False,
45
45
  min_seconds: int = 1,
@@ -52,7 +52,7 @@ def _pipes_lap(
52
52
  nopretty: bool = False,
53
53
  _progress: Optional['rich.progress.Progress'] = None,
54
54
  **kw: Any
55
- ) -> Tuple[List[meerschaum.Pipe], List[meerschaum.Pipe]]:
55
+ ) -> Tuple[List[mrsm.Pipe], List[mrsm.Pipe]]:
56
56
  """
57
57
  Do a lap of syncing pipes.
58
58
  """
@@ -402,11 +402,20 @@ def _wrap_pipe(
402
402
  Wrapper function for handling exceptions.
403
403
  """
404
404
  import time
405
+ import traceback
406
+ from datetime import datetime, timedelta, timezone
407
+ import meerschaum as mrsm
408
+ from meerschaum.utils.typing import is_success_tuple, SuccessTuple
405
409
  from meerschaum.connectors import get_connector_plugin
406
410
  from meerschaum.utils.venv import Venv
407
411
  from meerschaum.plugins import _pre_sync_hooks, _post_sync_hooks
408
412
  from meerschaum.utils.misc import filter_keywords
413
+ from meerschaum.utils.pool import get_pool
414
+ from meerschaum.utils.warnings import warn
415
+
416
+ pool = get_pool(workers=workers)
409
417
 
418
+ sync_timestamp = datetime.now(timezone.utc)
410
419
  sync_start = time.perf_counter()
411
420
  sync_kwargs = {k: v for k, v in kw.items() if k != 'blocking'}
412
421
  sync_kwargs.update({
@@ -415,8 +424,9 @@ def _wrap_pipe(
415
424
  'debug': debug,
416
425
  'min_seconds': min_seconds,
417
426
  'workers': workers,
418
- 'bounded': 'bounded',
427
+ 'bounded': bounded,
419
428
  'chunk_interval': chunk_interval,
429
+ 'sync_timestamp': sync_timestamp,
420
430
  })
421
431
  if not verify and not deduplicate:
422
432
  sync_method = pipe.sync
@@ -427,12 +437,32 @@ def _wrap_pipe(
427
437
  sync_kwargs['deduplicate'] = deduplicate
428
438
  sync_kwargs['sync_method'] = sync_method
429
439
 
430
- for module_name, pre_sync_hooks in _pre_sync_hooks.items():
431
- plugin_name = module_name.split('.')[-1] if module_name.startswith('plugins.') else None
440
+ def call_sync_hook(plugin_name: str, sync_hook) -> SuccessTuple:
432
441
  plugin = mrsm.Plugin(plugin_name) if plugin_name else None
433
- with Venv(plugin):
434
- for pre_sync_hook in pre_sync_hooks:
435
- _ = pre_sync_hook(pipe, **filter_keywords(pre_sync_hook, **sync_kwargs))
442
+ with mrsm.Venv(plugin):
443
+ try:
444
+ sync_hook_result = sync_hook(pipe, **filter_keywords(sync_hook, **sync_kwargs))
445
+ if is_success_tuple(sync_hook_result):
446
+ return sync_hook_result
447
+ except Exception as e:
448
+ msg = (
449
+ f"Failed to execute sync hook '{sync_hook.__name__}' "
450
+ + f"from plugin '{plugin}':\n{traceback.format_exc()}"
451
+ )
452
+ warn(msg, stack=False)
453
+ return False, msg
454
+ return True, "Success"
455
+
456
+ hook_results = []
457
+ def apply_hooks(is_pre_sync: bool):
458
+ _sync_hooks = (_pre_sync_hooks if is_pre_sync else _post_sync_hooks)
459
+ for module_name, sync_hooks in _sync_hooks.items():
460
+ plugin_name = module_name.split('.')[-1] if module_name.startswith('plugins.') else None
461
+ for sync_hook in sync_hooks:
462
+ hook_result = pool.apply_async(call_sync_hook, (plugin_name, sync_hook))
463
+ hook_results.append(hook_result)
464
+
465
+ apply_hooks(True)
436
466
 
437
467
  try:
438
468
  with Venv(get_connector_plugin(pipe.connector), debug=debug):
@@ -444,18 +474,16 @@ def _wrap_pipe(
444
474
  return_tuple = (False, f"Failed to sync {pipe} with exception:" + "\n" + str(e))
445
475
 
446
476
  duration = time.perf_counter() - sync_start
447
- sync_kwargs['duration'] = duration
448
- for module_name, post_sync_hooks in _post_sync_hooks.items():
449
- plugin_name = module_name.split('.')[-1] if module_name.startswith('plugins.') else None
450
- plugin = mrsm.Plugin(plugin_name) if plugin_name else None
451
- with Venv(plugin):
452
- for post_sync_hook in post_sync_hooks:
453
- _ = post_sync_hook(
454
- pipe,
455
- return_tuple,
456
- **filter_keywords(post_sync_hook, **sync_kwargs)
457
- )
458
-
477
+ sync_kwargs.update({
478
+ 'success_tuple': return_tuple,
479
+ 'sync_duration': duration,
480
+ 'sync_complete_timestamp': datetime.now(timezone.utc),
481
+ })
482
+ apply_hooks(False)
483
+ for hook_result in hook_results:
484
+ hook_success, hook_msg = hook_result.get()
485
+ mrsm.pprint((hook_success, hook_msg))
486
+
459
487
  return return_tuple
460
488
 
461
489
 
@@ -126,7 +126,6 @@ default_shell_config = {
126
126
  'timeout' : 60,
127
127
  'max_history' : 1000,
128
128
  'clear_screen' : True,
129
- 'cmd' : default_cmd,
130
129
  'bottom_toolbar' : {
131
130
  'enabled' : True,
132
131
  },
@@ -2,4 +2,4 @@
2
2
  Specify the Meerschaum release version.
3
3
  """
4
4
 
5
- __version__ = "2.1.5"
5
+ __version__ = "2.1.7"
@@ -174,9 +174,6 @@ def get_pipe_metadef(
174
174
  )
175
175
 
176
176
 
177
- if 'order by' in definition.lower() and 'over' not in definition.lower():
178
- error("Cannot fetch with an ORDER clause in the definition")
179
-
180
177
  apply_backtrack = begin == '' and check_existing
181
178
  backtrack_interval = pipe.get_backtrack_interval(check_existing=check_existing, debug=debug)
182
179
  btm = (
@@ -308,9 +305,9 @@ def _simple_fetch_query(pipe, debug: bool=False, **kw) -> str:
308
305
  def_name = 'definition'
309
306
  definition = get_pipe_query(pipe)
310
307
  return (
311
- f"WITH {def_name} AS ({definition}) SELECT * FROM {def_name}"
308
+ f"WITH {def_name} AS (\n{definition}\n) SELECT * FROM {def_name}"
312
309
  if pipe.connector.flavor not in ('mysql', 'mariadb')
313
- else f"SELECT * FROM ({definition}) AS {def_name}"
310
+ else f"SELECT * FROM (\n{definition}\n) AS {def_name}"
314
311
  )
315
312
 
316
313
  def _join_fetch_query(
@@ -363,10 +360,10 @@ def _join_fetch_query(
363
360
  )
364
361
  + f") AS {id_remote_name}, "
365
362
  + dateadd_str(
366
- flavor=pipe.connector.flavor,
367
- begin=_st,
368
- datepart='minute',
369
- number=pipe.parameters.get('fetch', {}).get('backtrack_minutes', 0)
363
+ flavor = pipe.connector.flavor,
364
+ begin = _st,
365
+ datepart = 'minute',
366
+ number = pipe.parameters.get('fetch', {}).get('backtrack_minutes', 0)
370
367
  ) + " AS " + dt_remote_name + "\nUNION ALL\n"
371
368
  )
372
369
  _sync_times_q = _sync_times_q[:(-1 * len('UNION ALL\n'))] + ")"
@@ -374,13 +371,13 @@ def _join_fetch_query(
374
371
  definition = get_pipe_query(pipe)
375
372
  query = (
376
373
  f"""
377
- WITH definition AS ({definition}){_sync_times_q}
374
+ WITH definition AS (\n{definition}\n){_sync_times_q}
378
375
  SELECT definition.*
379
376
  FROM definition"""
380
377
  if pipe.connector.flavor not in ('mysql', 'mariadb')
381
378
  else (
382
379
  f"""
383
- SELECT * FROM ({definition}) AS definition"""
380
+ SELECT * FROM (\n{definition}\n) AS definition"""
384
381
  )
385
382
  ) + f"""
386
383
  LEFT OUTER JOIN {sync_times_remote_name} AS st
@@ -385,7 +385,13 @@ def get_create_index_queries(
385
385
  -------
386
386
  A dictionary of column names mapping to lists of queries.
387
387
  """
388
- from meerschaum.utils.sql import sql_item_name, get_distinct_col_count, update_queries
388
+ from meerschaum.utils.sql import (
389
+ sql_item_name,
390
+ get_distinct_col_count,
391
+ update_queries,
392
+ get_null_replacement,
393
+ COALESCE_UNIQUE_INDEX_FLAVORS,
394
+ )
389
395
  from meerschaum.config import get_config
390
396
  index_queries = {}
391
397
 
@@ -497,15 +503,37 @@ def get_create_index_queries(
497
503
  if ix and ix in existing_cols_types
498
504
  ]
499
505
  )
506
+ coalesce_indices_cols_str = ', '.join(
507
+ [
508
+ (
509
+ "COALESCE("
510
+ + sql_item_name(ix, self.flavor)
511
+ + ", "
512
+ + get_null_replacement(existing_cols_types[ix], self.flavor)
513
+ + ") "
514
+ ) if ix_key != 'datetime' else (sql_item_name(ix, self.flavor))
515
+ for ix_key, ix in pipe.columns.items()
516
+ if ix and ix in existing_cols_types
517
+ ]
518
+ )
519
+ unique_index_name = sql_item_name(pipe.target + '_unique_index', self.flavor)
500
520
  constraint_name = sql_item_name(pipe.target + '_constraint', self.flavor)
501
- constraint_query = (
521
+ add_constraint_query = (
502
522
  f"ALTER TABLE {_pipe_name} ADD CONSTRAINT {constraint_name} UNIQUE ({indices_cols_str})"
503
- if self.flavor != 'sqlite'
504
- else f"CREATE UNIQUE INDEX {constraint_name} ON {_pipe_name} ({indices_cols_str})"
505
523
  )
524
+ unique_index_cols_str = (
525
+ indices_cols_str
526
+ if self.flavor not in COALESCE_UNIQUE_INDEX_FLAVORS
527
+ else coalesce_indices_cols_str
528
+ )
529
+ create_unique_index_query = (
530
+ f"CREATE UNIQUE INDEX {unique_index_name} ON {_pipe_name} ({unique_index_cols_str})"
531
+ )
532
+ constraint_queries = [create_unique_index_query]
533
+ if self.flavor != 'sqlite':
534
+ constraint_queries.append(add_constraint_query)
506
535
  if upsert and indices_cols_str:
507
- index_queries[constraint_name] = [constraint_query]
508
-
536
+ index_queries[unique_index_name] = constraint_queries
509
537
  return index_queries
510
538
 
511
539
 
@@ -1074,7 +1102,7 @@ def get_pipe_attributes(
1074
1102
  def sync_pipe(
1075
1103
  self,
1076
1104
  pipe: mrsm.Pipe,
1077
- df: Union[pandas.DataFrame, str, Dict[Any, Any], None] = None,
1105
+ df: Union[pd.DataFrame, str, Dict[Any, Any], None] = None,
1078
1106
  begin: Optional[datetime] = None,
1079
1107
  end: Optional[datetime] = None,
1080
1108
  chunksize: Optional[int] = -1,
@@ -1154,7 +1182,12 @@ def sync_pipe(
1154
1182
  dprint("Fetched data:\n" + str(df))
1155
1183
 
1156
1184
  if not isinstance(df, pd.DataFrame):
1157
- df = pipe.enforce_dtypes(df, chunksize=chunksize, debug=debug)
1185
+ df = pipe.enforce_dtypes(
1186
+ df,
1187
+ chunksize = chunksize,
1188
+ safe_copy = kw.get('safe_copy', False),
1189
+ debug = debug,
1190
+ )
1158
1191
 
1159
1192
  ### if table does not exist, create it with indices
1160
1193
  is_new = False
@@ -1198,6 +1231,7 @@ def sync_pipe(
1198
1231
  upsert = pipe.parameters.get('upsert', False) and (self.flavor + '-upsert') in update_queries
1199
1232
  if upsert:
1200
1233
  check_existing = False
1234
+ kw['safe_copy'] = kw.get('safe_copy', False)
1201
1235
 
1202
1236
  unseen_df, update_df, delta_df = (
1203
1237
  pipe.filter_existing(
@@ -1286,7 +1320,11 @@ def sync_pipe(
1286
1320
  temp_pipe = Pipe(
1287
1321
  pipe.connector_keys.replace(':', '_') + '_', pipe.metric_key, pipe.location_key,
1288
1322
  instance = pipe.instance_keys,
1289
- columns = pipe.columns,
1323
+ columns = {
1324
+ ix_key: ix
1325
+ for ix_key, ix in pipe.columns.items()
1326
+ if ix and ix in update_df.columns
1327
+ },
1290
1328
  dtypes = pipe.dtypes,
1291
1329
  target = temp_target,
1292
1330
  temporary = True,
@@ -214,22 +214,6 @@ def create_tables(
214
214
  from meerschaum.utils.sql import get_rename_table_queries, table_exists
215
215
  _tables = tables if tables is not None else get_tables(conn)
216
216
 
217
- rename_queries = []
218
- for table_key, table in _tables.items():
219
- if table_exists(
220
- table_key,
221
- conn,
222
- schema = conn.instance_schema,
223
- ):
224
- rename_queries.extend(get_rename_table_queries(
225
- table_key,
226
- table.name,
227
- schema = conn.instance_schema,
228
- flavor = conn.flavor,
229
- ))
230
- if rename_queries:
231
- conn.exec_queries(rename_queries)
232
-
233
217
  try:
234
218
  conn.metadata.create_all(bind=conn.engine)
235
219
  except Exception as e:
@@ -8,7 +8,7 @@ Retrieve Pipes' data from instances.
8
8
 
9
9
  from __future__ import annotations
10
10
  from datetime import datetime, timedelta
11
- from meerschaum.utils.typing import Optional, Dict, Any, Union, Generator, List, Tuple
11
+ from meerschaum.utils.typing import Optional, Dict, Any, Union, Generator, List, Tuple, Iterator
12
12
  from meerschaum.config import get_config
13
13
 
14
14
  def get_data(
@@ -247,7 +247,7 @@ def _get_data_as_iterator(
247
247
  fresh: bool = False,
248
248
  debug: bool = False,
249
249
  **kw: Any
250
- ) -> Generator['pd.DataFrame']:
250
+ ) -> Iterator['pd.DataFrame']:
251
251
  """
252
252
  Return a pipe's data as a generator.
253
253
  """
@@ -267,16 +267,17 @@ def _get_data_as_iterator(
267
267
 
268
268
  _ = kw.pop('as_chunks', None)
269
269
  _ = kw.pop('as_iterator', None)
270
+ dt_col = self.columns.get('datetime', None)
270
271
  min_dt = (
271
272
  begin
272
273
  if begin is not None
273
274
  else self.get_sync_time(round_down=False, newest=False, params=params, debug=debug)
274
- )
275
+ ) if dt_col else None
275
276
  max_dt = (
276
277
  end
277
278
  if end is not None
278
279
  else self.get_sync_time(round_down=False, newest=True, params=params, debug=debug)
279
- )
280
+ ) if dt_col else None
280
281
 
281
282
  ### We want to search just past the maximum value.
282
283
  if end is None:
@@ -14,6 +14,7 @@ def enforce_dtypes(
14
14
  self,
15
15
  df: 'pd.DataFrame',
16
16
  chunksize: Optional[int] = -1,
17
+ safe_copy: bool = True,
17
18
  debug: bool = False,
18
19
  ) -> 'pd.DataFrame':
19
20
  """
@@ -71,7 +72,7 @@ def enforce_dtypes(
71
72
  )
72
73
  return df
73
74
 
74
- return _enforce_dtypes(df, pipe_dtypes, debug=debug)
75
+ return _enforce_dtypes(df, pipe_dtypes, safe_copy=safe_copy, debug=debug)
75
76
 
76
77
 
77
78
  def infer_dtypes(self, persist: bool=False, debug: bool=False) -> Dict[str, Any]:
@@ -12,6 +12,7 @@ import json
12
12
  import time
13
13
  import threading
14
14
  import multiprocessing
15
+ import functools
15
16
  from datetime import datetime, timedelta
16
17
 
17
18
  from meerschaum.utils.typing import (
@@ -518,6 +519,8 @@ def exists(
518
519
  def filter_existing(
519
520
  self,
520
521
  df: 'pd.DataFrame',
522
+ safe_copy: bool = True,
523
+ date_bound_only: bool = False,
521
524
  chunksize: Optional[int] = -1,
522
525
  debug: bool = False,
523
526
  **kw
@@ -530,6 +533,14 @@ def filter_existing(
530
533
  df: 'pd.DataFrame'
531
534
  The dataframe to inspect and filter.
532
535
 
536
+ safe_copy: bool, default True
537
+ If `True`, create a copy before comparing and modifying the dataframes.
538
+ Setting to `False` may mutate the DataFrames.
539
+ See `meerschaum.utils.dataframe.filter_unseen_df`.
540
+
541
+ date_bound_only: bool, default False
542
+ If `True`, only use the datetime index to fetch the sample dataframe.
543
+
533
544
  chunksize: Optional[int], default -1
534
545
  The `chunksize` used when fetching existing data.
535
546
 
@@ -567,7 +578,8 @@ def filter_existing(
567
578
  else:
568
579
  merge = pd.merge
569
580
  NA = pd.NA
570
-
581
+ if df is None:
582
+ return df, df, df
571
583
  if (df.empty if not is_dask else len(df) == 0):
572
584
  return df, df, df
573
585
 
@@ -617,7 +629,7 @@ def filter_existing(
617
629
  traceback.print_exc()
618
630
  max_dt = None
619
631
 
620
- if not ('datetime' in str(type(max_dt))) or str(min_dt) == 'NaT':
632
+ if ('datetime' not in str(type(max_dt))) or str(min_dt) == 'NaT':
621
633
  if 'int' not in str(type(max_dt)).lower():
622
634
  max_dt = None
623
635
 
@@ -645,7 +657,7 @@ def filter_existing(
645
657
  col: df[col].unique()
646
658
  for col in self.columns
647
659
  if col in df.columns and col != dt_col
648
- }
660
+ } if not date_bound_only else {}
649
661
  filter_params_index_limit = get_config('pipes', 'sync', 'filter_params_index_limit')
650
662
  _ = kw.pop('params', None)
651
663
  params = {
@@ -655,7 +667,7 @@ def filter_existing(
655
667
  ]
656
668
  for col, unique_vals in unique_index_vals.items()
657
669
  if len(unique_vals) <= filter_params_index_limit
658
- }
670
+ } if not date_bound_only else {}
659
671
 
660
672
  if debug:
661
673
  dprint(f"Looking at data between '{begin}' and '{end}':", **kw)
@@ -698,18 +710,23 @@ def filter_existing(
698
710
  col: to_pandas_dtype(typ)
699
711
  for col, typ in self_dtypes.items()
700
712
  },
713
+ safe_copy = safe_copy,
701
714
  debug = debug
702
715
  ),
703
716
  on_cols_dtypes,
704
717
  )
705
718
 
706
719
  ### Cast dicts or lists to strings so we can merge.
720
+ serializer = functools.partial(json.dumps, sort_keys=True, separators=(',', ':'), default=str)
721
+ def deserializer(x):
722
+ return json.loads(x) if isinstance(x, str) else x
723
+
707
724
  unhashable_delta_cols = get_unhashable_cols(delta_df)
708
725
  unhashable_backtrack_cols = get_unhashable_cols(backtrack_df)
709
726
  for col in unhashable_delta_cols:
710
- delta_df[col] = delta_df[col].apply(json.dumps)
727
+ delta_df[col] = delta_df[col].apply(serializer)
711
728
  for col in unhashable_backtrack_cols:
712
- backtrack_df[col] = backtrack_df[col].apply(json.dumps)
729
+ backtrack_df[col] = backtrack_df[col].apply(serializer)
713
730
  casted_cols = set(unhashable_delta_cols + unhashable_backtrack_cols)
714
731
 
715
732
  joined_df = merge(
@@ -722,13 +739,9 @@ def filter_existing(
722
739
  ) if on_cols else delta_df
723
740
  for col in casted_cols:
724
741
  if col in joined_df.columns:
725
- joined_df[col] = joined_df[col].apply(
726
- lambda x: (
727
- json.loads(x)
728
- if isinstance(x, str)
729
- else x
730
- )
731
- )
742
+ joined_df[col] = joined_df[col].apply(deserializer)
743
+ if col in delta_df.columns:
744
+ delta_df[col] = delta_df[col].apply(deserializer)
732
745
 
733
746
  ### Determine which rows are completely new.
734
747
  new_rows_mask = (joined_df['_merge'] == 'left_only') if on_cols else None