instructure-dap-client 2.2.0b2__tar.gz → 2.3.0__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 (186) hide show
  1. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/LICENSE +22 -1
  2. instructure_dap_client-2.3.0/MANIFEST.in +11 -0
  3. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/PKG-INFO +20 -20
  4. instructure_dap_client-2.3.0/README.md +73 -0
  5. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/__init__.py +3 -3
  6. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/actions/init_db.py +3 -1
  7. instructure_dap_client-2.3.0/dap/actions/list_db.py +176 -0
  8. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/actions/sync_db.py +3 -1
  9. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/api.py +112 -51
  10. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/commands/command_group.py +3 -2
  11. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/commands/commands.py +48 -7
  12. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/concurrency.py +9 -13
  13. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/dap_error.py +1 -1
  14. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/dap_types.py +47 -15
  15. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/integration/base_connection.py +3 -1
  16. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/integration/database.py +7 -4
  17. instructure_dap_client-2.3.0/dap/integration/database_errors.py +116 -0
  18. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/log.py +45 -11
  19. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/replicator/meta_schema.py +1 -0
  20. instructure_dap_client-2.3.0/dap/replicator/record_validation.py +274 -0
  21. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/replicator/sql.py +3 -0
  22. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/replicator/sql_metatable_handler.py +4 -4
  23. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/replicator/sql_op.py +9 -4
  24. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/replicator/sql_op_init.py +32 -13
  25. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/replicator/sql_op_sync.py +54 -15
  26. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/retry_options.py +10 -3
  27. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/tracking.py +36 -16
  28. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/ui.py +90 -24
  29. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/instructure_dap_client.egg-info/PKG-INFO +20 -20
  30. instructure_dap_client-2.3.0/instructure_dap_client.egg-info/SOURCES.txt +172 -0
  31. instructure_dap_client-2.3.0/instructure_dap_client.egg-info/requires.txt +23 -0
  32. instructure_dap_client-2.3.0/instructure_dap_client.egg-info/top_level.txt +3 -0
  33. instructure_dap_client-2.3.0/pyproject.toml +134 -0
  34. instructure_dap_client-2.3.0/pysqlsync/__init__.py +17 -0
  35. instructure_dap_client-2.3.0/pysqlsync/base.py +1392 -0
  36. instructure_dap_client-2.3.0/pysqlsync/connection.py +93 -0
  37. instructure_dap_client-2.3.0/pysqlsync/data/__init__.py +0 -0
  38. instructure_dap_client-2.3.0/pysqlsync/data/exchange.py +192 -0
  39. instructure_dap_client-2.3.0/pysqlsync/data/generator.py +355 -0
  40. instructure_dap_client-2.3.0/pysqlsync/dialect/README.md +35 -0
  41. instructure_dap_client-2.3.0/pysqlsync/dialect/__init__.py +0 -0
  42. instructure_dap_client-2.3.0/pysqlsync/dialect/delta/__init__.py +0 -0
  43. instructure_dap_client-2.3.0/pysqlsync/dialect/delta/data_types.py +59 -0
  44. instructure_dap_client-2.3.0/pysqlsync/dialect/delta/dependency.py +9 -0
  45. instructure_dap_client-2.3.0/pysqlsync/dialect/delta/engine.py +26 -0
  46. instructure_dap_client-2.3.0/pysqlsync/dialect/delta/generator.py +82 -0
  47. instructure_dap_client-2.3.0/pysqlsync/dialect/delta/object_types.py +95 -0
  48. instructure_dap_client-2.3.0/pysqlsync/dialect/mssql/README.md +28 -0
  49. instructure_dap_client-2.3.0/pysqlsync/dialect/mssql/__init__.py +0 -0
  50. instructure_dap_client-2.3.0/pysqlsync/dialect/mssql/connection.py +154 -0
  51. instructure_dap_client-2.3.0/pysqlsync/dialect/mssql/data_types.py +127 -0
  52. instructure_dap_client-2.3.0/pysqlsync/dialect/mssql/dependency.py +11 -0
  53. instructure_dap_client-2.3.0/pysqlsync/dialect/mssql/discovery.py +107 -0
  54. instructure_dap_client-2.3.0/pysqlsync/dialect/mssql/engine.py +28 -0
  55. instructure_dap_client-2.3.0/pysqlsync/dialect/mssql/generator.py +96 -0
  56. instructure_dap_client-2.3.0/pysqlsync/dialect/mssql/mutation.py +54 -0
  57. instructure_dap_client-2.3.0/pysqlsync/dialect/mssql/object_types.py +143 -0
  58. instructure_dap_client-2.3.0/pysqlsync/dialect/mysql/__init__.py +0 -0
  59. instructure_dap_client-2.3.0/pysqlsync/dialect/mysql/connection.py +136 -0
  60. instructure_dap_client-2.3.0/pysqlsync/dialect/mysql/data_types.py +48 -0
  61. instructure_dap_client-2.3.0/pysqlsync/dialect/mysql/dependency.py +12 -0
  62. instructure_dap_client-2.3.0/pysqlsync/dialect/mysql/discovery.py +153 -0
  63. instructure_dap_client-2.3.0/pysqlsync/dialect/mysql/engine.py +28 -0
  64. instructure_dap_client-2.3.0/pysqlsync/dialect/mysql/generator.py +152 -0
  65. instructure_dap_client-2.3.0/pysqlsync/dialect/mysql/mutation.py +73 -0
  66. instructure_dap_client-2.3.0/pysqlsync/dialect/mysql/object_types.py +83 -0
  67. instructure_dap_client-2.3.0/pysqlsync/dialect/oracle/README.md +22 -0
  68. instructure_dap_client-2.3.0/pysqlsync/dialect/oracle/__init__.py +0 -0
  69. instructure_dap_client-2.3.0/pysqlsync/dialect/oracle/connection.py +149 -0
  70. instructure_dap_client-2.3.0/pysqlsync/dialect/oracle/data_types.py +112 -0
  71. instructure_dap_client-2.3.0/pysqlsync/dialect/oracle/dependency.py +11 -0
  72. instructure_dap_client-2.3.0/pysqlsync/dialect/oracle/discovery.py +239 -0
  73. instructure_dap_client-2.3.0/pysqlsync/dialect/oracle/engine.py +28 -0
  74. instructure_dap_client-2.3.0/pysqlsync/dialect/oracle/generator.py +101 -0
  75. instructure_dap_client-2.3.0/pysqlsync/dialect/oracle/mutation.py +45 -0
  76. instructure_dap_client-2.3.0/pysqlsync/dialect/oracle/object_types.py +67 -0
  77. instructure_dap_client-2.3.0/pysqlsync/dialect/postgresql/__init__.py +0 -0
  78. instructure_dap_client-2.3.0/pysqlsync/dialect/postgresql/connection.py +141 -0
  79. instructure_dap_client-2.3.0/pysqlsync/dialect/postgresql/data_types.py +14 -0
  80. instructure_dap_client-2.3.0/pysqlsync/dialect/postgresql/dependency.py +11 -0
  81. instructure_dap_client-2.3.0/pysqlsync/dialect/postgresql/discovery.py +400 -0
  82. instructure_dap_client-2.3.0/pysqlsync/dialect/postgresql/engine.py +28 -0
  83. instructure_dap_client-2.3.0/pysqlsync/dialect/postgresql/generator.py +107 -0
  84. instructure_dap_client-2.3.0/pysqlsync/dialect/postgresql/mutation.py +90 -0
  85. instructure_dap_client-2.3.0/pysqlsync/dialect/postgresql/object_types.py +107 -0
  86. instructure_dap_client-2.3.0/pysqlsync/dialect/redshift/__init__.py +0 -0
  87. instructure_dap_client-2.3.0/pysqlsync/dialect/redshift/connection.py +164 -0
  88. instructure_dap_client-2.3.0/pysqlsync/dialect/redshift/data_types.py +18 -0
  89. instructure_dap_client-2.3.0/pysqlsync/dialect/redshift/dependency.py +11 -0
  90. instructure_dap_client-2.3.0/pysqlsync/dialect/redshift/engine.py +28 -0
  91. instructure_dap_client-2.3.0/pysqlsync/dialect/redshift/generator.py +90 -0
  92. instructure_dap_client-2.3.0/pysqlsync/dialect/snowflake/__init__.py +0 -0
  93. instructure_dap_client-2.3.0/pysqlsync/dialect/snowflake/data_types.py +26 -0
  94. instructure_dap_client-2.3.0/pysqlsync/dialect/snowflake/dependency.py +11 -0
  95. instructure_dap_client-2.3.0/pysqlsync/dialect/snowflake/engine.py +26 -0
  96. instructure_dap_client-2.3.0/pysqlsync/dialect/snowflake/generator.py +72 -0
  97. instructure_dap_client-2.3.0/pysqlsync/dialect/snowflake/object_types.py +92 -0
  98. instructure_dap_client-2.3.0/pysqlsync/dialect/test/__init__.py +0 -0
  99. instructure_dap_client-2.3.0/pysqlsync/dialect/test/dependency.py +12 -0
  100. instructure_dap_client-2.3.0/pysqlsync/dialect/trino/__init__.py +0 -0
  101. instructure_dap_client-2.3.0/pysqlsync/dialect/trino/connection.py +94 -0
  102. instructure_dap_client-2.3.0/pysqlsync/dialect/trino/dependency.py +11 -0
  103. instructure_dap_client-2.3.0/pysqlsync/dialect/trino/discovery.py +29 -0
  104. instructure_dap_client-2.3.0/pysqlsync/dialect/trino/engine.py +28 -0
  105. instructure_dap_client-2.3.0/pysqlsync/dialect/trino/generator.py +39 -0
  106. instructure_dap_client-2.3.0/pysqlsync/factory.py +171 -0
  107. instructure_dap_client-2.3.0/pysqlsync/formation/__init__.py +0 -0
  108. instructure_dap_client-2.3.0/pysqlsync/formation/constraints.py +86 -0
  109. instructure_dap_client-2.3.0/pysqlsync/formation/data_types.py +200 -0
  110. instructure_dap_client-2.3.0/pysqlsync/formation/discovery.py +398 -0
  111. instructure_dap_client-2.3.0/pysqlsync/formation/inspection.py +186 -0
  112. instructure_dap_client-2.3.0/pysqlsync/formation/mutation.py +328 -0
  113. instructure_dap_client-2.3.0/pysqlsync/formation/object_dict.py +87 -0
  114. instructure_dap_client-2.3.0/pysqlsync/formation/object_types.py +885 -0
  115. instructure_dap_client-2.3.0/pysqlsync/formation/py_to_sql.py +1142 -0
  116. instructure_dap_client-2.3.0/pysqlsync/formation/sql_to_py.py +202 -0
  117. instructure_dap_client-2.3.0/pysqlsync/model/__init__.py +0 -0
  118. instructure_dap_client-2.3.0/pysqlsync/model/data_types.py +405 -0
  119. instructure_dap_client-2.3.0/pysqlsync/model/entity_types.py +76 -0
  120. instructure_dap_client-2.3.0/pysqlsync/model/id_types.py +201 -0
  121. instructure_dap_client-2.3.0/pysqlsync/model/key_types.py +46 -0
  122. instructure_dap_client-2.3.0/pysqlsync/model/properties.py +242 -0
  123. instructure_dap_client-2.3.0/pysqlsync/py.typed +0 -0
  124. instructure_dap_client-2.3.0/pysqlsync/python_types.py +202 -0
  125. instructure_dap_client-2.3.0/pysqlsync/resultset.py +107 -0
  126. instructure_dap_client-2.3.0/pysqlsync/util/__init__.py +0 -0
  127. instructure_dap_client-2.3.0/pysqlsync/util/dataclasses.py +80 -0
  128. instructure_dap_client-2.3.0/pysqlsync/util/dispatch.py +26 -0
  129. instructure_dap_client-2.3.0/pysqlsync/util/typing.py +6 -0
  130. instructure_dap_client-2.3.0/setup.cfg +4 -0
  131. instructure_dap_client-2.3.0/strong_typing/__init__.py +13 -0
  132. instructure_dap_client-2.3.0/strong_typing/auxiliary.py +214 -0
  133. instructure_dap_client-2.3.0/strong_typing/classdef.py +425 -0
  134. instructure_dap_client-2.3.0/strong_typing/core.py +40 -0
  135. instructure_dap_client-2.3.0/strong_typing/deserializer.py +974 -0
  136. instructure_dap_client-2.3.0/strong_typing/docstring.py +431 -0
  137. instructure_dap_client-2.3.0/strong_typing/exception.py +17 -0
  138. instructure_dap_client-2.3.0/strong_typing/inspection.py +1029 -0
  139. instructure_dap_client-2.3.0/strong_typing/mapping.py +34 -0
  140. instructure_dap_client-2.3.0/strong_typing/name.py +292 -0
  141. instructure_dap_client-2.3.0/strong_typing/py.typed +0 -0
  142. instructure_dap_client-2.3.0/strong_typing/schema.py +702 -0
  143. instructure_dap_client-2.3.0/strong_typing/serialization.py +116 -0
  144. instructure_dap_client-2.3.0/strong_typing/serializer.py +535 -0
  145. instructure_dap_client-2.3.0/strong_typing/topological.py +84 -0
  146. instructure_dap_client-2.2.0b2/MANIFEST.in +0 -1
  147. instructure_dap_client-2.2.0b2/README.md +0 -9
  148. instructure_dap_client-2.2.0b2/dap/counted.py +0 -60
  149. instructure_dap_client-2.2.0b2/dap/integration/database_errors.py +0 -43
  150. instructure_dap_client-2.2.0b2/dap/payload.py +0 -44
  151. instructure_dap_client-2.2.0b2/dap/timer.py +0 -50
  152. instructure_dap_client-2.2.0b2/instructure_dap_client.egg-info/SOURCES.txt +0 -64
  153. instructure_dap_client-2.2.0b2/instructure_dap_client.egg-info/requires.txt +0 -19
  154. instructure_dap_client-2.2.0b2/instructure_dap_client.egg-info/top_level.txt +0 -1
  155. instructure_dap_client-2.2.0b2/pyproject.toml +0 -5
  156. instructure_dap_client-2.2.0b2/setup.cfg +0 -67
  157. instructure_dap_client-2.2.0b2/setup.py +0 -4
  158. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/__main__.py +0 -0
  159. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/actions/__init__.py +0 -0
  160. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/actions/download_incremental.py +0 -0
  161. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/actions/download_schema.py +0 -0
  162. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/actions/download_snapshot.py +0 -0
  163. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/actions/drop_db.py +0 -0
  164. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/actions/list_tables.py +0 -0
  165. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/commands/__init__.py +0 -0
  166. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/commands/global_options.py +0 -0
  167. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/integration/__init__.py +0 -0
  168. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/integration/connection.py +0 -0
  169. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/networking.py +0 -0
  170. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/py.typed +0 -0
  171. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/replicator/__init__.py +0 -0
  172. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/replicator/canvas.py +0 -0
  173. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/replicator/canvas_logs.py +0 -0
  174. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/replicator/catalog.py +0 -0
  175. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/replicator/new_quizzes.py +0 -0
  176. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/replicator/sql_op_drop.py +0 -0
  177. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/timestamp.py +0 -0
  178. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/version_upgrade/__init__.py +0 -0
  179. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/version_upgrade/db_version_upgrader.py +0 -0
  180. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/version_upgrade/mssql_0_to_1.sql +0 -0
  181. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/version_upgrade/mysql_0_to_1.sql +0 -0
  182. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/dap/version_upgrade/postgresql_0_to_1.sql +0 -0
  183. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/instructure_dap_client.egg-info/dependency_links.txt +0 -0
  184. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/instructure_dap_client.egg-info/entry_points.txt +0 -0
  185. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/instructure_dap_client.egg-info/zip-safe +0 -0
  186. {instructure_dap_client-2.2.0b2 → instructure_dap_client-2.3.0}/pypi_index.md +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022-2024 Instructure, Inc.
3
+ Copyright (c) 2022-2026 Instructure, Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -19,3 +19,24 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
+
23
+
24
+ ===============================================================================
25
+ Third-party vendored components
26
+ ===============================================================================
27
+
28
+ This distribution bundles the following third-party packages as top-level
29
+ modules. Both are licensed under the MIT License, whose terms are reproduced
30
+ above, with the copyright notices below. The versions bundled are recorded in
31
+ `vendor/VENDORED.md`.
32
+
33
+ `strong_typing` -- derived from https://github.com/hunyadi/strong_typing
34
+
35
+ Copyright (c) 2021-2026 Levente Hunyadi
36
+ Copyright (c) 2021-2026 Instructure Inc.
37
+ Copyright (c) 2021 Kheiron Medical Technologies Ltd.
38
+
39
+ `pysqlsync` -- derived from https://github.com/hunyadi/pysqlsync
40
+
41
+ Copyright (c) 2023-2025 Levente Hunyadi
42
+ Copyright (c) 2026 Instructure, Inc.
@@ -0,0 +1,11 @@
1
+ include dap/version_upgrade/*.sql
2
+
3
+ # Vendored packages -- see vendor/VENDORED.md. `recursive-include` mirrors the
4
+ # forks' own MANIFEST files, so package data added by a future re-vendor (e.g. a
5
+ # new dialect README) is picked up without editing this file.
6
+ include strong_typing/py.typed
7
+ include pysqlsync/py.typed
8
+ recursive-include pysqlsync *.md
9
+
10
+ global-exclude *.pyc
11
+ global-exclude __pycache__/*
@@ -1,16 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: instructure-dap-client
3
- Version: 2.2.0b2
3
+ Version: 2.3.0
4
4
  Summary: Data Access Platform client library
5
- Maintainer: Instucture, Inc.
6
- Maintainer-email: dapclient@instructure.com
7
- License: MIT
8
- Project-URL: Changelog, https://community.canvaslms.com/t5/Canvas-Change-Log/2025-API-and-CLI-Change-Log/ta-p/626858
9
- Classifier: Development Status :: 4 - Beta
5
+ Maintainer-email: "Instructure, Inc." <dapclient@instructure.com>
6
+ License-Expression: MIT
7
+ Project-URL: Changelog, https://community.instructure.com/en/discussion/664378/2026-api-and-cli-change-log
8
+ Classifier: Development Status :: 5 - Production/Stable
10
9
  Classifier: Environment :: Console
11
10
  Classifier: Intended Audience :: Developers
12
11
  Classifier: Intended Audience :: End Users/Desktop
13
- Classifier: License :: OSI Approved :: MIT License
14
12
  Classifier: Operating System :: OS Independent
15
13
  Classifier: Programming Language :: Python :: 3
16
14
  Classifier: Programming Language :: Python :: 3.11
@@ -21,22 +19,24 @@ Classifier: Typing :: Typed
21
19
  Requires-Python: >=3.11
22
20
  Description-Content-Type: text/markdown
23
21
  License-File: LICENSE
24
- Requires-Dist: aiohttp>=3.8.4
25
- Requires-Dist: aiohttp-retry>=2.8.3
26
- Requires-Dist: aiofiles>=23.1.0
27
- Requires-Dist: asyncclick==8.1.8
28
- Requires-Dist: types-aiofiles>=23.1.0
29
- Requires-Dist: json_strong_typing>=0.3.9
30
- Requires-Dist: pysqlsync[tsv]>=0.8.4
31
- Requires-Dist: PyJWT>=2.7.0
32
- Requires-Dist: tsv2py>=0.7.1
33
- Requires-Dist: rich>=14.0.0
22
+ Requires-Dist: aiohttp==3.13.4
23
+ Requires-Dist: aiohttp-retry==2.9.1
24
+ Requires-Dist: aiofiles==25.1.0
25
+ Requires-Dist: asyncclick==8.1.8.0
26
+ Requires-Dist: PyJWT==2.13.0
27
+ Requires-Dist: tsv2py==0.7.1
28
+ Requires-Dist: rich==14.3.3
29
+ Requires-Dist: jsonschema==4.26.0
30
+ Requires-Dist: truststore==0.10.4
31
+ Requires-Dist: typing_extensions==4.16.0; python_version < "3.12"
34
32
  Provides-Extra: postgresql
35
- Requires-Dist: pysqlsync[postgresql]>=0.8.4; extra == "postgresql"
33
+ Requires-Dist: asyncpg==0.31.0; extra == "postgresql"
36
34
  Provides-Extra: mysql
37
- Requires-Dist: pysqlsync[mysql]>=0.8.4; extra == "mysql"
35
+ Requires-Dist: aiomysql==0.3.2; extra == "mysql"
36
+ Requires-Dist: PyMySQL[rsa]==1.2.0; extra == "mysql"
37
+ Requires-Dist: cryptography==50.0.0; extra == "mysql"
38
38
  Provides-Extra: mssql
39
- Requires-Dist: pysqlsync[mssql]>=0.8.4; extra == "mssql"
39
+ Requires-Dist: pyodbc==5.3.0; extra == "mssql"
40
40
  Dynamic: license-file
41
41
 
42
42
  # Data Access Platform Client Library
@@ -0,0 +1,73 @@
1
+ # Data Access Platform CLI & Client Library
2
+
3
+ Data Access Platform (DAP) acts as a single source of data for analytics at Instructure. It provides efficient access to data collected across various educational products in bulk with high fidelity and low latency, adhering to a canonical data model.
4
+
5
+ The DAP CLI and Client Library is a Python wrapper around the [DAP Query API](https://developerdocs.instructure.com/services/dap/query-api), allowing you to fetch an initial snapshot and incremental changes, or initialize a supported database and keep it synchronized with [data in DAP](https://developerdocs.instructure.com/services/dap/dataset).
6
+
7
+ See [DAP Client Library Documentation](https://developerdocs.instructure.com/services/dap/dap-cli-readme).
8
+
9
+ Docs source repo: https://github.com/instructure/data-access-platform-documentation.
10
+
11
+ For development guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md).
12
+
13
+ ## Generate lib-reference documentation
14
+
15
+ `util/markdown_generator.py` (in the repository root, not under `cli/`) generates the GitBook Markdown
16
+ client library reference — `lib-reference.md` — from the public API, the database replicator, the shared
17
+ types, and the exception hierarchy.
18
+
19
+ It depends on `instructure-markdown-doc`, hosted on the internal Artifactory (authentication required). It
20
+ is **not** declared as a project dependency: it would force Artifactory authentication on every
21
+ `pip install`. Instead it is installed on its own, after authenticating, only when generating the docs. In
22
+ CI this is handled by the [`cli-doc.yml`](../.github/workflows/cli-doc.yml) workflow.
23
+
24
+ ### Authenticate to Artifactory
25
+
26
+ `pip` needs to be pointed at the authenticated Artifactory PyPI index to resolve `instructure-markdown-doc`.
27
+
28
+ 1. Generate a personal **Identity Token** in the [JFrog UI](https://instructure.jfrog.io): click your
29
+ username (top right) → **Edit Profile** → **Generate an Identity Token**. Copy it.
30
+ 2. Export `ARTIFACTORY_INDEX_URL` in your shell profile (e.g. `~/.zshrc`), using your JFrog username (shown
31
+ under **Edit Profile**) as the username and the identity token as the password:
32
+
33
+ ```sh
34
+ export ARTIFACTORY_INDEX_URL="https://<jfrog-username>:<identity-token>@instructure.jfrog.io/artifactory/api/pypi/virtual-pypi/simple"
35
+ ```
36
+
37
+ Restart the shell (or `source` the profile) so the variable is set.
38
+
39
+ > The token grants access under your account — treat it like a password. Keep it in your shell profile or a
40
+ > secrets manager; do not commit it.
41
+
42
+ ### Run the generator
43
+
44
+ With `ARTIFACTORY_INDEX_URL` set, install `instructure-markdown-doc` into the workspace environment, then
45
+ run the generator from the **repository root** — the `cli.dap` imports resolve against `PYTHONPATH`, and the
46
+ output path is relative to the script:
47
+
48
+ ```sh
49
+ uv sync --all-packages --all-extras
50
+ uv pip install --index "$ARTIFACTORY_INDEX_URL" instructure-markdown-doc==0.2.1
51
+ PYTHONPATH=. uv run --no-sync python util/markdown_generator.py
52
+ ```
53
+
54
+ `--no-sync` on the last command is required: a plain `uv run` re-syncs the environment, and because sync is
55
+ exact it would uninstall the ad-hoc `instructure-markdown-doc` installed on the line before.
56
+
57
+ The generated Markdown is written to `util/markdown/target/`.
58
+
59
+ ### Automatic publishing on release
60
+
61
+ The [`cli-doc.yml`](../.github/workflows/cli-doc.yml) workflow runs the generator in CI. Its behavior
62
+ depends on the trigger:
63
+
64
+ - **Push/PR to `main`, or `workflow_dispatch`** — generates `lib-reference.md` and uploads it as a build
65
+ artifact (`generated-docs`). Nothing is published.
66
+ - **Version tag push (`v*`, e.g. `v2.2.0`)** — first verifies the tag matches `__version__` in
67
+ `cli/dap/__init__.py` (failing fast on a mistagged release), then generates the doc and **opens a PR**
68
+ against the [documentation repo](https://github.com/instructure/data-access-platform-documentation),
69
+ replacing its `docs/lib-reference.md`. If the generated doc is unchanged, no PR is opened.
70
+
71
+ The PR is authored by the "DAP Docs Updater" GitHub App (installed on the documentation repo with
72
+ `Contents: write` + `Pull requests: write`); a human reviews and merges it. The branch and commit carry
73
+ provenance back to the source tag and the Actions run.
@@ -6,9 +6,9 @@ to data collected across various educational products in bulk with high fidelity
6
6
  a canonical data model.
7
7
  """
8
8
 
9
- __version__ = "2.2.0b2"
10
- __copyright__ = "Copyright 2022-2025, Instructure, Inc."
9
+ __version__ = "2.3.0"
10
+ __copyright__ = "Copyright 2022-2026, Instructure, Inc."
11
11
  __license__ = "MIT"
12
12
  __maintainer__ = "Instructure, Inc."
13
13
  __email__ = "dapclient@instructure.com"
14
- __status__ = "Beta"
14
+ __status__ = "Production"
@@ -23,4 +23,6 @@ async def init_db(
23
23
  async def replicate_table_fn(namespace: str, table: str) -> None:
24
24
  await sql_replicator.initialize(namespace, table)
25
25
 
26
- await session.execute_operation_on_tables(namespace, table_names, "initdb", replicate_table_fn)
26
+ await session.execute_operation_on_tables(
27
+ namespace, table_names, "initdb", replicate_table_fn
28
+ )
@@ -0,0 +1,176 @@
1
+ import json
2
+ from datetime import datetime, timezone
3
+ from typing import Optional
4
+
5
+ from pysqlsync.model.id_types import PrefixedId, QualifiedId
6
+
7
+ from .. import ui
8
+ from ..api import DAPClient
9
+ from ..dap_types import Credentials
10
+ from ..integration.database import DatabaseConnection
11
+ from ..replicator import meta_schema
12
+ from ..replicator.sql_metatable_handler import (
13
+ get_table_meta_record,
14
+ get_table_names_in_namespace_from_meta,
15
+ )
16
+
17
+
18
+ def _count_from_clause(
19
+ dialect: str, target_schema: Optional[str], target_table: str
20
+ ) -> str:
21
+ if dialect == "mysql":
22
+ # MySQL has no schemas; pysqlsync uses namespace__table (ANSI_QUOTES mode is always enabled)
23
+ return str(PrefixedId(target_schema, target_table))
24
+ else:
25
+ # PostgreSQL and MSSQL use schema.table
26
+ return str(QualifiedId(target_schema, target_table))
27
+
28
+
29
+ def cell(value: str | None, fallback: str = "-") -> tuple[ui.MsgType, str]:
30
+ if value is not None:
31
+ return (ui.MsgType.TITLE, value)
32
+ return (ui.MsgType.INFO, fallback)
33
+
34
+
35
+ def calculate_age(timestamp: datetime) -> str:
36
+ # timestamp is naive but represents UTC time, so get current UTC time for comparison
37
+ now = (
38
+ datetime.now(timezone.utc).replace(tzinfo=None)
39
+ if timestamp.tzinfo is None
40
+ else datetime.now(timestamp.tzinfo)
41
+ )
42
+ age_seconds = int((now - timestamp).total_seconds())
43
+ days, remainder = divmod(age_seconds, 86400)
44
+ hours, remainder = divmod(remainder, 3600)
45
+ minutes, seconds = divmod(remainder, 60)
46
+ if days > 0:
47
+ return f"{days}d {hours}h" if hours > 0 else f"{days}d"
48
+ elif hours > 0:
49
+ return f"{hours}h {minutes}m" if minutes > 0 else f"{hours}h"
50
+ elif minutes > 0:
51
+ return f"{minutes}m {seconds}s" if seconds > 0 else f"{minutes}m"
52
+ else:
53
+ return f"{seconds}s"
54
+
55
+
56
+ async def list_db(
57
+ base_url: str,
58
+ credentials: Credentials,
59
+ connection_string: str,
60
+ namespace: str,
61
+ tracking: Optional[bool] = None,
62
+ omit_not_replicated: bool = False,
63
+ omit_record_count: bool = False,
64
+ ) -> None:
65
+ dap_tables_names = []
66
+ async with DAPClient(
67
+ base_url=base_url, credentials=credentials, tracking=tracking
68
+ ) as session:
69
+ dap_tables_names = await session.get_tables(namespace)
70
+ if not omit_record_count:
71
+ ui.info("Querying table record counts...")
72
+ db_connection = DatabaseConnection(connection_string)
73
+ async with db_connection.connection as base_connection:
74
+ explorer = db_connection.engine.create_explorer(base_connection)
75
+ await explorer.synchronize(modules=[meta_schema])
76
+ db_table_names = await get_table_names_in_namespace_from_meta(
77
+ base_connection, namespace
78
+ )
79
+ db_table_meta_records: dict[str, Optional[meta_schema.table_sync]] = {}
80
+ for table_name in sorted(db_table_names):
81
+ meta = await get_table_meta_record(
82
+ base_connection, namespace, table_name
83
+ )
84
+ if meta is not None:
85
+ db_table_meta_records[table_name] = meta
86
+ if not omit_not_replicated:
87
+ for table_name in sorted(dap_tables_names):
88
+ if table_name not in db_table_meta_records:
89
+ db_table_meta_records[table_name] = None
90
+
91
+ db_table_record_counts = {}
92
+ for table_name, meta in db_table_meta_records.items():
93
+ if meta is not None and not omit_record_count:
94
+ from_clause = _count_from_clause(
95
+ db_connection.dialect, meta.target_schema, meta.target_table
96
+ )
97
+ count = await base_connection.query_one(
98
+ int,
99
+ f"SELECT COUNT(*) FROM {from_clause}",
100
+ )
101
+ db_table_record_counts[table_name] = count
102
+
103
+ title_str = f"Local database replication status of tables in namespace [bold]{namespace}[/bold]"
104
+ columns = [
105
+ ui.TableColumn("Name", style="bold"),
106
+ *(
107
+ []
108
+ if omit_record_count
109
+ else [ui.TableColumn("Records", style="bold", justify="right")]
110
+ ),
111
+ ui.TableColumn("Data as of"),
112
+ ui.TableColumn("Staleness"),
113
+ ui.TableColumn("Schema", justify="right"),
114
+ ui.TableColumn("Use command"),
115
+ ]
116
+ ui.print_table(
117
+ title=title_str,
118
+ columns=columns,
119
+ rows=[
120
+ [
121
+ cell(table_name if meta is not None else None, table_name),
122
+ *(
123
+ []
124
+ if omit_record_count
125
+ else [
126
+ cell(
127
+ str(db_table_record_counts[table_name])
128
+ if table_name in db_table_record_counts
129
+ else None
130
+ )
131
+ ]
132
+ ),
133
+ cell(
134
+ meta.timestamp.replace(tzinfo=timezone.utc)
135
+ .astimezone()
136
+ .isoformat()
137
+ if meta is not None
138
+ else None,
139
+ "Not replicated",
140
+ ),
141
+ cell(
142
+ calculate_age(meta.timestamp) if meta is not None else None
143
+ ),
144
+ cell(str(meta.schema_version) if meta is not None else None),
145
+ cell("syncdb" if meta is not None else None, "initdb"),
146
+ ]
147
+ for table_name, meta in db_table_meta_records.items()
148
+ ],
149
+ )
150
+ if not ui.is_interactive():
151
+ rows = []
152
+ for table_name, meta in db_table_meta_records.items():
153
+ row: dict = {
154
+ "name": table_name,
155
+ "timestamp": meta.timestamp.replace(tzinfo=timezone.utc)
156
+ .astimezone()
157
+ .isoformat()
158
+ if meta is not None
159
+ else None,
160
+ "staleness": calculate_age(meta.timestamp)
161
+ if meta is not None
162
+ else None,
163
+ "schema": meta.schema_version if meta is not None else None,
164
+ "command": "syncdb" if meta is not None else "initdb",
165
+ }
166
+ if not omit_record_count:
167
+ row["records"] = db_table_record_counts.get(table_name)
168
+ rows.append(row)
169
+ print(json.dumps(rows, indent=2))
170
+
171
+ if session.tracking_data:
172
+ session.tracking_data.set_cmd_info("listdb", namespace, None)
173
+ session.tracking_data.db_dialect = db_connection.dialect
174
+ session.tracking_data.db_version = await db_connection.get_version(
175
+ db_connection.dialect, base_connection
176
+ )
@@ -23,4 +23,6 @@ async def sync_db(
23
23
  async def replicate_table_fn(namespace: str, table: str) -> None:
24
24
  await sql_replicator.synchronize(namespace, table)
25
25
 
26
- await session.execute_operation_on_tables(namespace, table_names, "syncdb", replicate_table_fn)
26
+ await session.execute_operation_on_tables(
27
+ namespace, table_names, "syncdb", replicate_table_fn
28
+ )