globus-cli 3.27.0__tar.gz → 3.28.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 (267) hide show
  1. {globus-cli-3.27.0/src/globus_cli.egg-info → globus_cli-3.28.0}/PKG-INFO +2 -2
  2. {globus-cli-3.27.0 → globus_cli-3.28.0}/pyproject.toml +1 -1
  3. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/__init__.py +1 -0
  4. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/_common.py +46 -7
  5. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/create.py +2 -2
  6. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/run/resume.py +4 -2
  7. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/update.py +9 -2
  8. globus_cli-3.28.0/src/globus_cli/commands/flows/validate.py +106 -0
  9. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/timer/create/transfer.py +2 -2
  10. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/timer/resume.py +4 -2
  11. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/exception_handling/hooks.py +109 -0
  12. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/login_manager/manager.py +10 -4
  13. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/commands.py +4 -0
  14. globus_cli-3.28.0/src/globus_cli/services/auth.py +83 -0
  15. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/termio/printer.py +8 -1
  16. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/version.py +1 -1
  17. {globus-cli-3.27.0 → globus_cli-3.28.0/src/globus_cli.egg-info}/PKG-INFO +2 -2
  18. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli.egg-info/SOURCES.txt +1 -0
  19. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli.egg-info/requires.txt +1 -1
  20. globus-cli-3.27.0/src/globus_cli/services/auth.py +0 -163
  21. {globus-cli-3.27.0 → globus_cli-3.28.0}/LICENSE +0 -0
  22. {globus-cli-3.27.0 → globus_cli-3.28.0}/README.rst +0 -0
  23. {globus-cli-3.27.0 → globus_cli-3.28.0}/setup.cfg +0 -0
  24. {globus-cli-3.27.0 → globus_cli-3.28.0}/setup.py +0 -0
  25. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/__init__.py +0 -0
  26. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/__main__.py +0 -0
  27. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/_warnings.py +0 -0
  28. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/__init__.py +0 -0
  29. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/_common.py +0 -0
  30. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/api.py +0 -0
  31. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/bookmark/__init__.py +0 -0
  32. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/bookmark/_common.py +0 -0
  33. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/bookmark/create.py +0 -0
  34. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/bookmark/delete.py +0 -0
  35. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/bookmark/list.py +0 -0
  36. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/bookmark/rename.py +0 -0
  37. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/bookmark/show.py +0 -0
  38. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/cli_profile_list.py +0 -0
  39. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/collection/__init__.py +0 -0
  40. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/collection/_common.py +0 -0
  41. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/collection/create/__init__.py +0 -0
  42. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/collection/create/guest.py +0 -0
  43. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/collection/create/mapped.py +0 -0
  44. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/collection/delete.py +0 -0
  45. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/collection/list.py +0 -0
  46. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/collection/show.py +0 -0
  47. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/collection/update.py +0 -0
  48. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/delete.py +0 -0
  49. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/__init__.py +0 -0
  50. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/_common.py +0 -0
  51. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/activate.py +0 -0
  52. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/create.py +0 -0
  53. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/deactivate.py +0 -0
  54. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/delete.py +0 -0
  55. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/is_activated.py +0 -0
  56. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/local_id.py +0 -0
  57. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/my_shared_endpoint_list.py +0 -0
  58. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/permission/__init__.py +0 -0
  59. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/permission/_common.py +0 -0
  60. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/permission/create.py +0 -0
  61. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/permission/delete.py +0 -0
  62. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/permission/list.py +0 -0
  63. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/permission/show.py +0 -0
  64. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/permission/update.py +0 -0
  65. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/role/__init__.py +0 -0
  66. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/role/_common.py +0 -0
  67. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/role/create.py +0 -0
  68. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/role/delete.py +0 -0
  69. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/role/list.py +0 -0
  70. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/role/show.py +0 -0
  71. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/search.py +0 -0
  72. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/server/__init__.py +0 -0
  73. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/server/_common.py +0 -0
  74. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/server/add.py +0 -0
  75. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/server/delete.py +0 -0
  76. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/server/list.py +0 -0
  77. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/server/show.py +0 -0
  78. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/server/update.py +0 -0
  79. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/set_subscription_id.py +0 -0
  80. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/show.py +0 -0
  81. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/storage_gateway/__init__.py +0 -0
  82. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/storage_gateway/list.py +0 -0
  83. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/update.py +0 -0
  84. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/user_credential/__init__.py +0 -0
  85. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/user_credential/_common.py +0 -0
  86. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/user_credential/create/__init__.py +0 -0
  87. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/user_credential/create/from_json.py +0 -0
  88. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/user_credential/create/posix.py +0 -0
  89. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/user_credential/create/s3.py +0 -0
  90. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/user_credential/delete.py +0 -0
  91. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/user_credential/list.py +0 -0
  92. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/user_credential/show.py +0 -0
  93. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/user_credential/update/__init__.py +0 -0
  94. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/endpoint/user_credential/update/from_json.py +0 -0
  95. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/delete.py +0 -0
  96. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/list.py +0 -0
  97. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/run/__init__.py +0 -0
  98. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/run/cancel.py +0 -0
  99. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/run/delete.py +0 -0
  100. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/run/list.py +0 -0
  101. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/run/show.py +0 -0
  102. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/run/show_definition.py +0 -0
  103. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/run/show_logs.py +0 -0
  104. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/run/update.py +0 -0
  105. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/show.py +0 -0
  106. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/flows/start.py +0 -0
  107. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcp/__init__.py +0 -0
  108. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcp/create/__init__.py +0 -0
  109. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcp/create/_common.py +0 -0
  110. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcp/create/guest.py +0 -0
  111. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcp/create/mapped.py +0 -0
  112. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcp/set_subscription_id.py +0 -0
  113. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcp/update/__init__.py +0 -0
  114. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcp/update/guest.py +0 -0
  115. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcp/update/mapped.py +0 -0
  116. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcs/__init__.py +0 -0
  117. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcs/endpoint/__init__.py +0 -0
  118. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcs/endpoint/_common.py +0 -0
  119. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcs/endpoint/role/__init__.py +0 -0
  120. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcs/endpoint/role/_common.py +0 -0
  121. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcs/endpoint/role/create.py +0 -0
  122. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcs/endpoint/role/delete.py +0 -0
  123. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcs/endpoint/role/list.py +0 -0
  124. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcs/endpoint/role/show.py +0 -0
  125. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcs/endpoint/set_subscription_id.py +0 -0
  126. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcs/endpoint/show.py +0 -0
  127. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/gcs/endpoint/update.py +0 -0
  128. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/get_identities.py +0 -0
  129. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/__init__.py +0 -0
  130. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/_common.py +0 -0
  131. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/create.py +0 -0
  132. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/delete.py +0 -0
  133. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/get_by_subscription.py +0 -0
  134. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/get_subscription_info.py +0 -0
  135. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/invite/__init__.py +0 -0
  136. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/invite/_common.py +0 -0
  137. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/invite/accept.py +0 -0
  138. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/invite/decline.py +0 -0
  139. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/join.py +0 -0
  140. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/leave.py +0 -0
  141. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/list.py +0 -0
  142. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/member/__init__.py +0 -0
  143. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/member/add.py +0 -0
  144. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/member/approve.py +0 -0
  145. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/member/invite.py +0 -0
  146. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/member/list.py +0 -0
  147. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/member/reject.py +0 -0
  148. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/member/remove.py +0 -0
  149. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/set_policies.py +0 -0
  150. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/show.py +0 -0
  151. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/group/update.py +0 -0
  152. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/list_commands.py +0 -0
  153. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/login.py +0 -0
  154. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/logout.py +0 -0
  155. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/ls.py +0 -0
  156. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/mkdir.py +0 -0
  157. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/rename.py +0 -0
  158. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/rm.py +0 -0
  159. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/__init__.py +0 -0
  160. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/_common.py +0 -0
  161. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/delete_by_query.py +0 -0
  162. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/index/__init__.py +0 -0
  163. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/index/create.py +0 -0
  164. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/index/delete.py +0 -0
  165. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/index/list.py +0 -0
  166. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/index/role/__init__.py +0 -0
  167. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/index/role/create.py +0 -0
  168. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/index/role/delete.py +0 -0
  169. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/index/role/list.py +0 -0
  170. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/index/show.py +0 -0
  171. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/ingest.py +0 -0
  172. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/query.py +0 -0
  173. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/subject/__init__.py +0 -0
  174. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/subject/delete.py +0 -0
  175. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/subject/show.py +0 -0
  176. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/task/__init__.py +0 -0
  177. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/task/list.py +0 -0
  178. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/search/task/show.py +0 -0
  179. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/session/__init__.py +0 -0
  180. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/session/consent.py +0 -0
  181. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/session/show.py +0 -0
  182. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/session/update.py +0 -0
  183. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/stat.py +0 -0
  184. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/task/__init__.py +0 -0
  185. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/task/_common.py +0 -0
  186. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/task/cancel.py +0 -0
  187. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/task/event_list.py +0 -0
  188. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/task/generate_submission_id.py +0 -0
  189. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/task/list.py +0 -0
  190. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/task/pause_info.py +0 -0
  191. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/task/show.py +0 -0
  192. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/task/update.py +0 -0
  193. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/task/wait.py +0 -0
  194. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/timer/__init__.py +0 -0
  195. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/timer/_common.py +0 -0
  196. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/timer/create/__init__.py +0 -0
  197. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/timer/delete.py +0 -0
  198. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/timer/list.py +0 -0
  199. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/timer/pause.py +0 -0
  200. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/timer/show.py +0 -0
  201. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/transfer.py +0 -0
  202. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/update.py +0 -0
  203. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/version.py +0 -0
  204. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/commands/whoami.py +0 -0
  205. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/constants.py +0 -0
  206. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/endpointish/__init__.py +0 -0
  207. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/endpointish/endpointish.py +0 -0
  208. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/endpointish/entity_type.py +0 -0
  209. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/endpointish/errors.py +0 -0
  210. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/exception_handling/__init__.py +0 -0
  211. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/exception_handling/registry.py +0 -0
  212. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/globus_cli_flake8.py +0 -0
  213. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/login_manager/__init__.py +0 -0
  214. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/login_manager/_old_config.py +0 -0
  215. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/login_manager/auth_flows.py +0 -0
  216. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/login_manager/client_login.py +0 -0
  217. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/login_manager/context.py +0 -0
  218. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/login_manager/errors.py +0 -0
  219. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/login_manager/local_server.py +0 -0
  220. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/login_manager/scopes.py +0 -0
  221. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/login_manager/tokenstore.py +0 -0
  222. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/login_manager/utils.py +0 -0
  223. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/__init__.py +0 -0
  224. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/command_state.py +0 -0
  225. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/mutex_group.py +0 -0
  226. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/param_classes.py +0 -0
  227. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/param_types/__init__.py +0 -0
  228. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/param_types/delimited.py +0 -0
  229. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/param_types/endpoint_plus_path.py +0 -0
  230. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/param_types/identity_type.py +0 -0
  231. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/param_types/json_strorfile.py +0 -0
  232. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/param_types/location.py +0 -0
  233. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/param_types/notify_param.py +0 -0
  234. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/param_types/nullable.py +0 -0
  235. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/param_types/task_path.py +0 -0
  236. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/param_types/timedelta.py +0 -0
  237. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/shared_callbacks.py +0 -0
  238. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/shared_options/__init__.py +0 -0
  239. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/shared_options/endpointish.py +0 -0
  240. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/shared_options/id_args.py +0 -0
  241. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/shared_options/transfer_task_options.py +0 -0
  242. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/parsing/shell_completion.py +0 -0
  243. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/reflect.py +0 -0
  244. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/services/__init__.py +0 -0
  245. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/services/gcs.py +0 -0
  246. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/services/transfer/__init__.py +0 -0
  247. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/services/transfer/activation.py +0 -0
  248. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/services/transfer/client.py +0 -0
  249. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/services/transfer/data.py +0 -0
  250. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/services/transfer/delegate_proxy.py +0 -0
  251. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/services/transfer/recursive_ls.py +0 -0
  252. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/termio/__init__.py +0 -0
  253. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/termio/awscli_text.py +0 -0
  254. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/termio/context.py +0 -0
  255. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/termio/errors.py +0 -0
  256. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/termio/field.py +0 -0
  257. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/termio/formatters/__init__.py +0 -0
  258. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/termio/formatters/auth.py +0 -0
  259. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/termio/formatters/base.py +0 -0
  260. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/termio/formatters/compound.py +0 -0
  261. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/termio/formatters/primitive.py +0 -0
  262. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/termio/server_timing.py +0 -0
  263. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/types.py +0 -0
  264. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli/utils.py +0 -0
  265. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli.egg-info/dependency_links.txt +0 -0
  266. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli.egg-info/entry_points.txt +0 -0
  267. {globus-cli-3.27.0 → globus_cli-3.28.0}/src/globus_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: globus-cli
3
- Version: 3.27.0
3
+ Version: 3.28.0
4
4
  Summary: Globus CLI
5
5
  Author-email: Stephen Rosen <sirosen@globus.org>
6
6
  Project-URL: homepage, https://github.com/globus/globus-cli
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
18
18
  Requires-Python: >=3.8
19
19
  Description-Content-Type: text/x-rst
20
20
  License-File: LICENSE
21
- Requires-Dist: globus-sdk==3.39.0
21
+ Requires-Dist: globus-sdk==3.41.0
22
22
  Requires-Dist: click<9,>=8.1.4
23
23
  Requires-Dist: jmespath==1.0.1
24
24
  Requires-Dist: packaging>=17.0
@@ -28,7 +28,7 @@ classifiers = [
28
28
  ]
29
29
  requires-python = ">=3.8"
30
30
  dependencies = [
31
- "globus-sdk==3.39.0",
31
+ "globus-sdk==3.41.0",
32
32
  "click>=8.1.4,<9",
33
33
  "jmespath==1.0.1",
34
34
  "packaging>=17.0",
@@ -6,6 +6,7 @@ from globus_cli.parsing import group
6
6
  lazy_subcommands={
7
7
  "create": (".create", "create_command"),
8
8
  "update": (".update", "update_command"),
9
+ "validate": (".validate", "validate_command"),
9
10
  "delete": (".delete", "delete_command"),
10
11
  "list": (".list", "list_command"),
11
12
  "show": (".show", "show_command"),
@@ -1,12 +1,13 @@
1
+ from __future__ import annotations
2
+
3
+ import typing as t
4
+ import uuid
5
+
1
6
  import click
2
7
 
3
8
  from globus_cli.parsing import JSONStringOrFile
4
9
 
5
- input_schema_option = click.option(
6
- "--input-schema",
7
- "input_schema",
8
- type=JSONStringOrFile(),
9
- help="""
10
+ _input_schema_helptext = """
10
11
  The JSON input schema that governs the parameters
11
12
  used to start the flow.
12
13
 
@@ -21,11 +22,22 @@ input_schema_option = click.option(
21
22
 
22
23
  \b
23
24
  --input-schema schema.json
25
+ """
24
26
 
25
- If unspecified, the default is an empty JSON object ('{}').
26
- """,
27
+ input_schema_option = click.option(
28
+ "--input-schema",
29
+ "input_schema",
30
+ type=JSONStringOrFile(),
31
+ help=_input_schema_helptext,
27
32
  )
28
33
 
34
+ input_schema_option_with_default = click.option(
35
+ "--input-schema",
36
+ "input_schema",
37
+ type=JSONStringOrFile(),
38
+ help=_input_schema_helptext
39
+ + "\n If unspecified, the default is an empty JSON object ('{}').",
40
+ )
29
41
 
30
42
  subtitle_option = click.option(
31
43
  "--subtitle",
@@ -102,3 +114,30 @@ keywords_option = click.option(
102
114
  to create a list of keywords.
103
115
  """,
104
116
  )
117
+
118
+
119
+ class SubscriptionIdType(click.ParamType):
120
+ name = "SUBSCRIPTION_ID"
121
+
122
+ def convert(
123
+ self, value: str, param: click.Parameter | None, ctx: click.Context | None
124
+ ) -> uuid.UUID | t.Literal["DEFAULT"]:
125
+ if value.upper() == "DEFAULT":
126
+ return "DEFAULT"
127
+ try:
128
+ return uuid.UUID(value)
129
+ except ValueError:
130
+ self.fail(f"{value} must be either a UUID or 'DEFAULT'", param, ctx)
131
+
132
+
133
+ subscription_id_option = click.option(
134
+ "--subscription-id",
135
+ "subscription_id",
136
+ type=SubscriptionIdType(),
137
+ multiple=False,
138
+ help="""
139
+ A subscription ID to assign to the flow.
140
+
141
+ The value may be a UUID or the word "DEFAULT".
142
+ """,
143
+ )
@@ -7,7 +7,7 @@ import click
7
7
  from globus_cli.commands.flows._common import (
8
8
  administrators_option,
9
9
  description_option,
10
- input_schema_option,
10
+ input_schema_option_with_default,
11
11
  keywords_option,
12
12
  starters_option,
13
13
  subtitle_option,
@@ -31,7 +31,7 @@ ROLE_TYPES = ("flow_viewer", "flow_starter", "flow_administrator", "flow_owner")
31
31
  type=JSONStringOrFile(),
32
32
  metavar="DEFINITION",
33
33
  )
34
- @input_schema_option
34
+ @input_schema_option_with_default
35
35
  @subtitle_option
36
36
  @description_option
37
37
  @administrators_option
@@ -129,5 +129,7 @@ def _has_required_consent(
129
129
  ) -> bool:
130
130
  auth_client = login_manager.get_auth_client()
131
131
  user_identity_id = get_current_identity_id()
132
- consents = auth_client.get_consents(user_identity_id)
133
- return consents.contains_scopes(required_scopes)
132
+ consents = auth_client.get_consents(user_identity_id).to_forest()
133
+ # FIXME: this type ignore is temporary because `meets_scope_requirements()`
134
+ # has too tight of a type
135
+ return consents.meets_scope_requirements(required_scopes) # type: ignore[arg-type]
@@ -1,12 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import typing as t
3
4
  import uuid
4
5
 
5
6
  import click
7
+ from globus_sdk.utils import MISSING
6
8
 
7
9
  from globus_cli.commands.flows._common import (
8
10
  description_option,
9
- input_schema_option,
11
+ input_schema_option_with_default,
12
+ subscription_id_option,
10
13
  subtitle_option,
11
14
  )
12
15
  from globus_cli.login_manager import LoginManager
@@ -60,7 +63,7 @@ ROLE_TYPES = ("flow_viewer", "flow_starter", "flow_administrator", "flow_owner")
60
63
  )
61
64
  @subtitle_option
62
65
  @description_option
63
- @input_schema_option
66
+ @input_schema_option_with_default
64
67
  @click.option(
65
68
  "--administrators",
66
69
  type=CommaDelimitedList(),
@@ -104,6 +107,7 @@ ROLE_TYPES = ("flow_viewer", "flow_starter", "flow_administrator", "flow_owner")
104
107
  Passing an empty string will clear any existing keywords.
105
108
  """,
106
109
  )
110
+ @subscription_id_option
107
111
  @LoginManager.requires_login("flows")
108
112
  def update_command(
109
113
  login_manager: LoginManager,
@@ -119,6 +123,7 @@ def update_command(
119
123
  starters: list[str] | None,
120
124
  viewers: list[str] | None,
121
125
  keywords: list[str] | None,
126
+ subscription_id: uuid.UUID | t.Literal["DEFAULT"] | None,
122
127
  ) -> None:
123
128
  """
124
129
  Update a flow.
@@ -154,6 +159,7 @@ def update_command(
154
159
  flow_starters=starters,
155
160
  flow_viewers=viewers,
156
161
  keywords=keywords,
162
+ subscription_id=subscription_id or MISSING,
157
163
  )
158
164
 
159
165
  # Configure formatters for principals
@@ -170,6 +176,7 @@ def update_command(
170
176
  Field("Description", "description"),
171
177
  Field("Keywords", "keywords", formatter=formatters.ArrayFormatter()),
172
178
  Field("Owner", "flow_owner", formatter=principal_formatter),
179
+ Field("Subscription ID", "subscription_id"),
173
180
  Field("Created At", "created_at", formatter=formatters.Date),
174
181
  Field("Updated At", "updated_at", formatter=formatters.Date),
175
182
  Field(
@@ -0,0 +1,106 @@
1
+ from __future__ import annotations
2
+
3
+ import click
4
+ import globus_sdk
5
+
6
+ from globus_cli.commands.flows._common import input_schema_option
7
+ from globus_cli.login_manager import LoginManager
8
+ from globus_cli.parsing import JSONStringOrFile, ParsedJSONData, command
9
+ from globus_cli.termio import Field, display
10
+
11
+
12
+ @command("validate", short_help="Validate a flow definition")
13
+ @click.argument(
14
+ "definition",
15
+ type=JSONStringOrFile(),
16
+ metavar="DEFINITION",
17
+ )
18
+ @input_schema_option
19
+ @LoginManager.requires_login("flows")
20
+ def validate_command(
21
+ login_manager: LoginManager,
22
+ *,
23
+ definition: ParsedJSONData,
24
+ input_schema: ParsedJSONData | None,
25
+ ) -> None:
26
+ """
27
+ Validate a flow definition (BETA).
28
+
29
+ DEFINITION is the JSON document that defines the flow's instructions.
30
+ The definition document may be specified inline, or it may be
31
+ a path to a JSON file.
32
+
33
+ Example: Inline JSON:
34
+
35
+ \b
36
+ globus flows validate \\
37
+ '{{"StartAt": "a", "States": {{"a": {{"Type": "Pass", "End": true}}}}}}'
38
+
39
+ Example: Path to JSON file:
40
+
41
+ \b
42
+ globus flows validate definition.json
43
+ """
44
+ payload = {}
45
+
46
+ # Ensure that the definition is a JSON object
47
+ if not isinstance(definition.data, dict):
48
+ raise click.UsageError("Flow definition must be a JSON object")
49
+ payload["definition"] = definition.data
50
+
51
+ # Ensure the input schema, if provided, is a JSON object
52
+ if input_schema is not None:
53
+ if not isinstance(input_schema.data, dict):
54
+ raise click.UsageError("--input-schema must be a JSON object")
55
+ payload["input_schema"] = input_schema.data
56
+
57
+ # Configure client
58
+ flows_client = login_manager.get_flows_client()
59
+
60
+ res = flows_client.validate_flow(**payload)
61
+
62
+ display(
63
+ res,
64
+ text_mode=_validate_flow_output_handler,
65
+ )
66
+
67
+
68
+ def _validate_flow_output_handler(result: globus_sdk.GlobusHTTPResponse) -> None:
69
+ # Discovered scopes output
70
+ analysis_response = result.get("analysis")
71
+ # Beta API: Defend against the case where 'analysis' is no longer returned
72
+ if analysis_response is not None:
73
+ _validate_flow_analysis_output_handler(analysis_response)
74
+ click.echo()
75
+
76
+ scopes_response = result.get("scopes")
77
+ # Beta API: Defend against the case where 'scopes' is no longer returned
78
+ if scopes_response is not None:
79
+ _validate_flow_scope_output_handler(scopes_response)
80
+
81
+
82
+ def _validate_flow_scope_output_handler(scopes_response: dict[str, str]) -> None:
83
+ # Always include User and Flow scopes
84
+ scopes_fields = [Field("RunAs", "RunAs"), Field("Scope", "scope")]
85
+ scope_entries = [
86
+ {"RunAs": k, "scope": scope}
87
+ for k, scopes in scopes_response.items()
88
+ for scope in scopes
89
+ ]
90
+ click.echo("Discovered Scopes")
91
+ click.echo("=================")
92
+ if scope_entries:
93
+ display.render_table(
94
+ scope_entries,
95
+ fields=scopes_fields,
96
+ )
97
+ else:
98
+ click.echo("No scopes discovered")
99
+
100
+
101
+ def _validate_flow_analysis_output_handler(analysis_response: dict[str, str]) -> None:
102
+ # Always include the analysis output
103
+ click.echo("Analysis")
104
+ click.echo("========")
105
+ if count := analysis_response.get("number_of_possibilities"):
106
+ click.echo(f"Possible State Traversals: {count}")
@@ -315,13 +315,13 @@ def _derive_missing_scopes(
315
315
  user_identity_id = get_current_identity_id()
316
316
 
317
317
  # get the user's Globus CLI consents
318
- consents = auth_client.get_consents(user_identity_id)
318
+ consents = auth_client.get_consents(user_identity_id).to_forest()
319
319
 
320
320
  # check the 'needs_data_access' scope names against the 3rd-order dependencies
321
321
  # of the Timer scope and record the names of the ones which we need to request
322
322
  will_request_data_access: list[str] = []
323
323
  for name, scope_object in scopes_needed.items():
324
- if not consents.contains_scopes([scope_object]):
324
+ if not consents.meets_scope_requirements([str(scope_object)]):
325
325
  will_request_data_access.append(name)
326
326
 
327
327
  # return these ultimately filtered requirements
@@ -122,5 +122,7 @@ def _has_required_consent(
122
122
  ) -> bool:
123
123
  auth_client = login_manager.get_auth_client()
124
124
  user_identity_id = get_current_identity_id()
125
- consents = auth_client.get_consents(user_identity_id)
126
- return consents.contains_scopes(required_scopes)
125
+ consents = auth_client.get_consents(user_identity_id).to_forest()
126
+ # FIXME: this type ignore is temporary because `meets_scope_requirements()`
127
+ # has too tight of a type
128
+ return consents.meets_scope_requirements(required_scopes) # type: ignore[arg-type]
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
+ import textwrap
4
5
  import typing as t
5
6
 
6
7
  import click
@@ -20,6 +21,38 @@ def _pretty_json(data: dict, compact: bool = False) -> str:
20
21
  return json.dumps(data, indent=2, separators=(",", ": "), sort_keys=True)
21
22
 
22
23
 
24
+ _JSONPATH_SPECIAL_CHARS = "[]'\"\\."
25
+ _JSONPATH_ESCAPE_MAP = str.maketrans(
26
+ {
27
+ "'": "\\'",
28
+ "\\": "\\\\",
29
+ }
30
+ )
31
+
32
+
33
+ def _jsonpath_from_pydantic_loc(loc: list[str | int]) -> str:
34
+ """
35
+ Given a 'loc' from pydantic error data, convert it into a JSON Path expression.
36
+
37
+ Takes the following steps:
38
+ - turns integers into integer indices
39
+ - turns most strings into dotted access
40
+ - turns strings containing special characters into single-quoted bracket access
41
+ with ' and \\ escaped
42
+ """
43
+ path = "$"
44
+ for part in loc:
45
+ if isinstance(part, int):
46
+ path += f"[{part}]"
47
+ else:
48
+ if any(c in part for c in _JSONPATH_SPECIAL_CHARS):
49
+ part = f"'{part.translate(_JSONPATH_ESCAPE_MAP)}'"
50
+ path += f"[{part}]"
51
+ else:
52
+ path += f".{part}"
53
+ return path
54
+
55
+
23
56
  _DEFAULT_SESSION_REAUTH_MESSAGE = (
24
57
  "The resource you are trying to access requires you to re-authenticate."
25
58
  )
@@ -271,6 +304,82 @@ def searchapi_hook(exception: globus_sdk.SearchAPIError) -> None:
271
304
  write_error_info("Search API Error", fields)
272
305
 
273
306
 
307
+ @sdk_error_handler(
308
+ error_class="FlowsAPIError",
309
+ condition=lambda err: err.code == "UNPROCESSABLE_ENTITY",
310
+ )
311
+ def flows_validation_error_hook(exception: globus_sdk.FlowsAPIError) -> None:
312
+ message_string: str = exception.raw_json["error"]["message"] # type: ignore[index]
313
+ details: str | list[dict[str, t.Any]] = exception.raw_json["error"]["detail"] # type: ignore[index] # noqa: E501
314
+ message_fields = [PrintableErrorField("message", message_string)]
315
+
316
+ # conditionally do this work if there are multiple details
317
+ if isinstance(details, list) and len(details) > 1:
318
+ num_errors = len(details)
319
+ # try to extract 'loc' and 'msg' from the details, but only
320
+ # update 'message_fields' if the data are present
321
+ try:
322
+ messages = [
323
+ f"{_jsonpath_from_pydantic_loc(data['loc'])}: {data['msg']}"
324
+ for data in details
325
+ ]
326
+ except KeyError:
327
+ pass
328
+ else:
329
+ message_fields = [
330
+ PrintableErrorField(
331
+ "message", f"{num_errors} validation errors", multiline=True
332
+ ),
333
+ PrintableErrorField(
334
+ "errors",
335
+ "\n".join(messages),
336
+ multiline=True,
337
+ ),
338
+ ]
339
+
340
+ write_error_info(
341
+ "Flows API Error",
342
+ [
343
+ PrintableErrorField("HTTP status", exception.http_status),
344
+ PrintableErrorField("code", exception.code),
345
+ *message_fields,
346
+ ],
347
+ )
348
+
349
+
350
+ @sdk_error_handler(error_class="FlowsAPIError")
351
+ def flows_error_hook(exception: globus_sdk.FlowsAPIError) -> None:
352
+ details: list[dict[str, t.Any]] | str = exception.raw_json["error"]["detail"] # type: ignore[index] # noqa: E501
353
+ detail_fields: list[PrintableErrorField] = []
354
+
355
+ # if the detail is a string, return that as a single field
356
+ if isinstance(details, str):
357
+ if len(details) > 120:
358
+ details = textwrap.fill(details, width=80)
359
+ detail_fields = [PrintableErrorField("detail", details, multiline=True)]
360
+ # if it's a list of objects, wrap them into a multiline detail field
361
+ else:
362
+ detail_fields = [
363
+ PrintableErrorField(
364
+ "detail",
365
+ "\n".join(_pretty_json(detail, compact=True) for detail in details),
366
+ multiline=True,
367
+ )
368
+ ]
369
+
370
+ write_error_info(
371
+ "Flows API Error",
372
+ [
373
+ PrintableErrorField("HTTP status", exception.http_status),
374
+ PrintableErrorField("code", exception.code),
375
+ PrintableErrorField(
376
+ "message", exception.raw_json["error"]["message"] # type: ignore[index]
377
+ ),
378
+ *detail_fields,
379
+ ],
380
+ )
381
+
382
+
274
383
  @sdk_error_handler(
275
384
  error_class="AuthAPIError",
276
385
  condition=lambda err: err.message == "invalid_grant",
@@ -7,6 +7,7 @@ import uuid
7
7
 
8
8
  import click
9
9
  import globus_sdk
10
+ from globus_sdk.experimental.consents import ConsentForest
10
11
  from globus_sdk.experimental.scope_parser import Scope
11
12
  from globus_sdk.scopes import (
12
13
  AuthScopes,
@@ -37,7 +38,7 @@ from .tokenstore import (
37
38
  from .utils import get_current_identity_id, is_remote_session
38
39
 
39
40
  if t.TYPE_CHECKING:
40
- from ..services.auth import ConsentForestResponse, CustomAuthClient
41
+ from ..services.auth import CustomAuthClient
41
42
  from ..services.gcs import CustomGCSClient
42
43
  from ..services.transfer import CustomTransferClient
43
44
 
@@ -178,14 +179,19 @@ class LoginManager:
178
179
  else:
179
180
  # If there are dependent scopes all required scope paths are present in the
180
181
  # user's cached consent forest.
181
- return self._cached_consent_forest.contains_scopes(required_scopes)
182
+ #
183
+ # FIXME: this type ignore is temporary because `meets_scope_requirements()`
184
+ # has too tight of a type
185
+ return self._cached_consent_forest.meets_scope_requirements(
186
+ required_scopes # type: ignore[arg-type]
187
+ )
182
188
 
183
189
  @property
184
190
  @functools.lru_cache(maxsize=1) # noqa: B019
185
- def _cached_consent_forest(self) -> ConsentForestResponse:
191
+ def _cached_consent_forest(self) -> ConsentForest:
186
192
  identity_id = get_current_identity_id()
187
193
 
188
- return self.get_auth_client().get_consents(identity_id)
194
+ return self.get_auth_client().get_consents(identity_id).to_forest()
189
195
 
190
196
  def run_login_flow(
191
197
  self,
@@ -212,6 +212,10 @@ class TopLevelGroup(GlobusCommandGroup):
212
212
  def invoke(self, ctx: click.Context) -> t.Any:
213
213
  try:
214
214
  return super().invoke(ctx)
215
+ # explicitly re-raise any IOError (e.g. broken pipe) in order to engage
216
+ # click's existing handling of broken pipes
217
+ except OSError:
218
+ raise
215
219
  except Exception:
216
220
  # mypy thinks that exc_info could be (None, None, None), but... nope. False.
217
221
  custom_except_hook(sys.exc_info()) # type: ignore[arg-type]
@@ -0,0 +1,83 @@
1
+ from __future__ import annotations
2
+
3
+ import typing as t
4
+ import uuid
5
+
6
+ import globus_sdk
7
+ import globus_sdk.scopes
8
+
9
+
10
+ def _is_uuid(s: str) -> bool:
11
+ try:
12
+ uuid.UUID(s)
13
+ return True
14
+ except ValueError:
15
+ return False
16
+
17
+
18
+ class GetIdentitiesKwargs(t.TypedDict, total=False):
19
+ provision: bool
20
+ usernames: str
21
+ ids: str
22
+
23
+
24
+ class CustomAuthClient(globus_sdk.AuthClient):
25
+ def _lookup_identity_field(
26
+ self,
27
+ id_name: str | None = None,
28
+ id_id: str | None = None,
29
+ field: t.Literal["id", "username"] = "id",
30
+ provision: bool = False,
31
+ ) -> str | None:
32
+ assert (id_name or id_id) and not (id_name and id_id)
33
+
34
+ kw: GetIdentitiesKwargs = dict(provision=provision)
35
+ if id_name:
36
+ kw["usernames"] = id_name
37
+ elif id_id:
38
+ kw["ids"] = id_id
39
+ else:
40
+ raise NotImplementedError("must provide id or name")
41
+
42
+ try:
43
+ value = self.get_identities(**kw)["identities"][0][field]
44
+ # capture any failure to lookup this data, including:
45
+ # - identity doesn't exist (`identities=[]`)
46
+ # - field is missing
47
+ except LookupError:
48
+ return None
49
+
50
+ if not isinstance(value, str):
51
+ return None
52
+
53
+ return value
54
+
55
+ # this method has been added in the latest SDK versions but is not
56
+ # present in the last release
57
+ # this is therefore temporary until an SDK release ships with the change
58
+ # so that CLI testing against SDK main can succeed
59
+ if not hasattr(globus_sdk.AuthClient, "userinfo"):
60
+ userinfo = globus_sdk.AuthClient.oauth2_userinfo
61
+
62
+ @t.overload
63
+ def maybe_lookup_identity_id(
64
+ self, identity_name: str, provision: t.Literal[True]
65
+ ) -> str: ...
66
+
67
+ @t.overload
68
+ def maybe_lookup_identity_id(
69
+ self, identity_name: str, provision: bool = False
70
+ ) -> str | None: ...
71
+
72
+ def maybe_lookup_identity_id(
73
+ self, identity_name: str, provision: bool = False
74
+ ) -> str | None:
75
+ if _is_uuid(identity_name):
76
+ return identity_name
77
+ else:
78
+ return self._lookup_identity_field(
79
+ id_name=identity_name, provision=provision
80
+ )
81
+
82
+ def lookup_identity_name(self, identity_id: str) -> str | None:
83
+ return self._lookup_identity_field(id_id=identity_id, field="username")
@@ -170,7 +170,6 @@ class Renderer:
170
170
  TABLE = TextMode.text_table
171
171
  SILENT = TextMode.silent
172
172
  JSON = TextMode.json
173
- TABLE = TextMode.text_table
174
173
  RECORD = TextMode.text_record
175
174
  RECORD_LIST = TextMode.text_record_list
176
175
  RAW = TextMode.text_raw
@@ -200,6 +199,14 @@ class Renderer:
200
199
  sort_json_keys=sort_json_keys,
201
200
  )
202
201
 
202
+ def render_table(
203
+ self,
204
+ iterable: t.Iterable[t.Any],
205
+ fields: list[Field],
206
+ print_headers: bool = True,
207
+ ) -> None:
208
+ print_table(iterable, fields, print_headers)
209
+
203
210
 
204
211
  display = Renderer()
205
212
 
@@ -7,7 +7,7 @@ if t.TYPE_CHECKING:
7
7
 
8
8
  # single source of truth for package version,
9
9
  # see https://packaging.python.org/en/latest/single_source_version/
10
- __version__ = "3.27.0"
10
+ __version__ = "3.28.0"
11
11
 
12
12
  # app name to send as part of SDK requests
13
13
  app_name = f"Globus CLI v{__version__}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: globus-cli
3
- Version: 3.27.0
3
+ Version: 3.28.0
4
4
  Summary: Globus CLI
5
5
  Author-email: Stephen Rosen <sirosen@globus.org>
6
6
  Project-URL: homepage, https://github.com/globus/globus-cli
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
18
18
  Requires-Python: >=3.8
19
19
  Description-Content-Type: text/x-rst
20
20
  License-File: LICENSE
21
- Requires-Dist: globus-sdk==3.39.0
21
+ Requires-Dist: globus-sdk==3.41.0
22
22
  Requires-Dist: click<9,>=8.1.4
23
23
  Requires-Dist: jmespath==1.0.1
24
24
  Requires-Dist: packaging>=17.0
@@ -105,6 +105,7 @@ src/globus_cli/commands/flows/list.py
105
105
  src/globus_cli/commands/flows/show.py
106
106
  src/globus_cli/commands/flows/start.py
107
107
  src/globus_cli/commands/flows/update.py
108
+ src/globus_cli/commands/flows/validate.py
108
109
  src/globus_cli/commands/flows/run/__init__.py
109
110
  src/globus_cli/commands/flows/run/cancel.py
110
111
  src/globus_cli/commands/flows/run/delete.py
@@ -1,4 +1,4 @@
1
- globus-sdk==3.39.0
1
+ globus-sdk==3.41.0
2
2
  click<9,>=8.1.4
3
3
  jmespath==1.0.1
4
4
  packaging>=17.0