rucio-clients 37.7.1__tar.gz → 38.0.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.

Potentially problematic release.


This version of rucio-clients might be problematic. Click here for more details.

Files changed (205) hide show
  1. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/AUTHORS.rst +1 -0
  2. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/PKG-INFO +2 -2
  3. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/bin/rucio +2 -1
  4. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/etc/rucio.cfg.template +2 -2
  5. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/alembicrevision.py +1 -1
  6. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/bin_legacy/rucio.py +51 -107
  7. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/bin_legacy/rucio_admin.py +26 -26
  8. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/command.py +1 -0
  9. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/did.py +2 -2
  10. rucio_clients-38.0.0/lib/rucio/cli/opendata.py +132 -0
  11. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/replica.py +15 -5
  12. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/rule.py +7 -2
  13. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/scope.py +3 -2
  14. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/utils.py +28 -4
  15. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/baseclient.py +9 -1
  16. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/client.py +2 -0
  17. rucio_clients-38.0.0/lib/rucio/client/diracclient.py +124 -0
  18. rucio_clients-38.0.0/lib/rucio/client/opendataclient.py +249 -0
  19. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/subscriptionclient.py +30 -0
  20. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/uploadclient.py +10 -13
  21. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/constants.py +4 -1
  22. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/exception.py +55 -0
  23. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/plugins.py +45 -8
  24. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/schema/generic.py +5 -3
  25. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/schema/generic_multi_vo.py +4 -2
  26. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/types.py +8 -7
  27. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/utils.py +176 -11
  28. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/protocol.py +9 -5
  29. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/translation.py +17 -6
  30. rucio_clients-38.0.0/lib/rucio/vcsversion.py +11 -0
  31. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio_clients.egg-info/SOURCES.txt +3 -1
  32. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/pyproject.toml +5 -6
  33. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/requirements/requirements.client.txt +1 -1
  34. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/setuputil.py +42 -30
  35. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_abacus_account.py +1 -2
  36. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_abacus_collection_replica.py +5 -6
  37. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_abacus_rse.py +1 -2
  38. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_bin_rucio.py +31 -4
  39. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_cli_client_structure.py +48 -6
  40. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_conveyor.py +2 -0
  41. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_dataset_replicas.py +20 -5
  42. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_hermes.py +17 -27
  43. rucio_clients-38.0.0/tests/test_opendata.py +573 -0
  44. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_policy_package.py +42 -0
  45. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_reaper.py +1 -0
  46. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_replica.py +3 -4
  47. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rse.py +1 -2
  48. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rule.py +3 -4
  49. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_subscription.py +44 -16
  50. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_transfer_plugins.py +2 -2
  51. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_upload.py +93 -2
  52. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_utils.py +82 -6
  53. rucio_clients-37.7.1/lib/rucio/client/diracclient.py +0 -63
  54. rucio_clients-37.7.1/lib/rucio/client/fileclient.py +0 -57
  55. rucio_clients-37.7.1/lib/rucio/vcsversion.py +0 -11
  56. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/ChangeLog +0 -0
  57. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/LICENSE +0 -0
  58. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/MANIFEST.in +0 -0
  59. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/README.md +0 -0
  60. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/bin/rucio-admin +0 -0
  61. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/etc/rse-accounts.cfg.template +0 -0
  62. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/etc/rucio.cfg.atlas.client.template +0 -0
  63. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/__init__.py +0 -0
  64. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/__init__.py +0 -0
  65. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/account.py +0 -0
  66. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/bin_legacy/__init__.py +0 -0
  67. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/config.py +0 -0
  68. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/download.py +0 -0
  69. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/lifetime_exception.py +0 -0
  70. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/rse.py +0 -0
  71. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/subscription.py +0 -0
  72. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/cli/upload.py +0 -0
  73. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/__init__.py +0 -0
  74. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/accountclient.py +0 -0
  75. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/accountlimitclient.py +0 -0
  76. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/configclient.py +0 -0
  77. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/credentialclient.py +0 -0
  78. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/didclient.py +0 -0
  79. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/downloadclient.py +0 -0
  80. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/exportclient.py +0 -0
  81. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/importclient.py +0 -0
  82. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/lifetimeclient.py +0 -0
  83. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/lockclient.py +0 -0
  84. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/metaconventionsclient.py +0 -0
  85. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/pingclient.py +0 -0
  86. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/replicaclient.py +0 -0
  87. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/requestclient.py +0 -0
  88. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/richclient.py +0 -0
  89. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/rseclient.py +0 -0
  90. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/ruleclient.py +0 -0
  91. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/scopeclient.py +0 -0
  92. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/client/touchclient.py +0 -0
  93. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/__init__.py +0 -0
  94. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/bittorrent.py +0 -0
  95. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/cache.py +0 -0
  96. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/checksum.py +0 -0
  97. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/client.py +0 -0
  98. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/config.py +0 -0
  99. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/constraints.py +0 -0
  100. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/didtype.py +0 -0
  101. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/extra.py +0 -0
  102. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/logging.py +0 -0
  103. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/pcache.py +0 -0
  104. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/policy.py +0 -0
  105. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/schema/__init__.py +0 -0
  106. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/stomp_utils.py +0 -0
  107. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/stopwatch.py +0 -0
  108. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/common/test_rucio_server.py +0 -0
  109. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/__init__.py +0 -0
  110. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/__init__.py +0 -0
  111. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/bittorrent.py +0 -0
  112. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/cache.py +0 -0
  113. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/dummy.py +0 -0
  114. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/gfal.py +0 -0
  115. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/globus.py +0 -0
  116. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/http_cache.py +0 -0
  117. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/mock.py +0 -0
  118. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/ngarc.py +0 -0
  119. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/posix.py +0 -0
  120. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/rclone.py +0 -0
  121. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/rfio.py +0 -0
  122. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/srm.py +0 -0
  123. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/ssh.py +0 -0
  124. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/storm.py +0 -0
  125. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/webdav.py +0 -0
  126. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/protocols/xrootd.py +0 -0
  127. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/rse/rsemanager.py +0 -0
  128. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/lib/rucio/version.py +0 -0
  129. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/setup.cfg +0 -0
  130. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/setup.py +0 -0
  131. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_account.py +0 -0
  132. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_account_limits.py +0 -0
  133. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_archive.py +0 -0
  134. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_auditor.py +0 -0
  135. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_auditor_hdfs.py +0 -0
  136. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_auditor_srmdumps.py +0 -0
  137. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_authentication.py +0 -0
  138. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_automatix.py +0 -0
  139. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_bad_replica.py +0 -0
  140. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_bb8.py +0 -0
  141. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_belleii.py +0 -0
  142. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_boolean.py +0 -0
  143. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_clients.py +0 -0
  144. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_config.py +0 -0
  145. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_conveyor_submitter.py +0 -0
  146. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_counter.py +0 -0
  147. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_credential.py +0 -0
  148. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_curl.py +0 -0
  149. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_daemons.py +0 -0
  150. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_db.py +0 -0
  151. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_did.py +0 -0
  152. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_did_meta_plugins.py +0 -0
  153. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_download.py +0 -0
  154. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_dumper.py +0 -0
  155. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_filter_engine.py +0 -0
  156. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_gateway_external_representation.py +0 -0
  157. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_heartbeat.py +0 -0
  158. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_identity.py +0 -0
  159. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_impl_upload_download.py +0 -0
  160. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_import_export.py +0 -0
  161. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_judge_cleaner.py +0 -0
  162. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_judge_evaluator.py +0 -0
  163. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_judge_injector.py +0 -0
  164. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_judge_repairer.py +0 -0
  165. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_lifetime.py +0 -0
  166. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_message.py +0 -0
  167. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_meta_conventions.py +0 -0
  168. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_meta_did.py +0 -0
  169. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_module_import.py +0 -0
  170. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_monitor.py +0 -0
  171. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_multi_vo.py +0 -0
  172. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_naming_convention.py +0 -0
  173. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_oauthmanager.py +0 -0
  174. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_oidc.py +0 -0
  175. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_permission.py +0 -0
  176. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_pfns.py +0 -0
  177. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_ping.py +0 -0
  178. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_preparer.py +0 -0
  179. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_qos.py +0 -0
  180. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_quarantined_replica.py +0 -0
  181. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_redirect.py +0 -0
  182. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_replica_recoverer.py +0 -0
  183. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_replica_sorting.py +0 -0
  184. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_request.py +0 -0
  185. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_root_proxy.py +0 -0
  186. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rse_expression_parser.py +0 -0
  187. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rse_lfn2path.py +0 -0
  188. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rse_protocol_gfal2.py +0 -0
  189. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rse_protocol_gfal2_impl.py +0 -0
  190. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rse_protocol_posix.py +0 -0
  191. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rse_protocol_rclone.py +0 -0
  192. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rse_protocol_rsync.py +0 -0
  193. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rse_protocol_srm.py +0 -0
  194. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rse_protocol_ssh.py +0 -0
  195. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rse_protocol_webdav.py +0 -0
  196. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rse_protocol_xrootd.py +0 -0
  197. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rse_selector.py +0 -0
  198. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_rucio_server.py +0 -0
  199. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_scope.py +0 -0
  200. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_throttler.py +0 -0
  201. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_tpc.py +0 -0
  202. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_trace.py +0 -0
  203. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_transfer.py +0 -0
  204. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tests/test_undertaker.py +0 -0
  205. {rucio_clients-37.7.1 → rucio_clients-38.0.0}/tools/merge_rucio_configs.py +0 -0
@@ -69,6 +69,7 @@ Individual contributors to the source code
69
69
  - Maximilian Linhoff, <maximilian.linhoff@tu-dortmund.de>, 2024
70
70
  - Eric Banzuzi, <eric.banzuzi@gmail.com>, 2024
71
71
  - Paul Millar, <paul.millar@desy.de>, 2025
72
+ - Vimalan S <vimalan.github@gmail.com>, 2025
72
73
 
73
74
  Organisations employing contributors
74
75
  ------------------------------------
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rucio-clients
3
- Version: 37.7.1
3
+ Version: 38.0.0
4
4
  Summary: Rucio Client Lite Package
5
5
  Home-page: https://rucio.cern.ch/
6
6
  Author: Rucio
@@ -22,7 +22,7 @@ License-File: AUTHORS.rst
22
22
  Requires-Dist: click
23
23
  Requires-Dist: requests
24
24
  Requires-Dist: urllib3
25
- Requires-Dist: dogpile-cache<=1.2.2
25
+ Requires-Dist: dogpile-cache
26
26
  Requires-Dist: packaging
27
27
  Requires-Dist: tabulate
28
28
  Requires-Dist: jsonschema
@@ -54,6 +54,7 @@ def map_legacy_command() -> Optional[list[str]]:
54
54
  command_map = {
55
55
  "list-file-replicas": ["replica", "list", "file"],
56
56
  "list-dataset-replicas": ["replica", "list", "dataset"],
57
+ "list-datasets-rse": ["replica", "list", "dataset", "--rse"],
57
58
  "add-dataset": ["did", "add", "--type dataset"],
58
59
  "add-container": ["did", "add", "--type container"],
59
60
  "attach": ["did", "content", "add"],
@@ -96,7 +97,7 @@ def map_legacy_command() -> Optional[list[str]]:
96
97
 
97
98
 
98
99
  if __name__ == "__main__":
99
- commands = ("account", "config", "did", "replica", "rse", "rule", "scope", "subscription", "ping", "whoami", "test-server", "lifetime-exception", "upload", "download")
100
+ commands = ("account", "config", "did", "replica", "rse", "rule", "scope", "subscription", "ping", "whoami", "test-server", "lifetime-exception", "upload", "download", "opendata")
100
101
 
101
102
  parser = argparse.ArgumentParser(add_help=False)
102
103
  # Check for legacy flag
@@ -73,8 +73,8 @@ default_mail_from = spamspamspam@cern.ch
73
73
 
74
74
  [database]
75
75
  default = sqlite:////tmp/rucio.db
76
- #default = oracle://_____________:___________@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=_________)(PORT=______))(ADDRESS=(PROTOCOL=TCP)(HOST=_________)(PORT=_____))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=__________)))
77
- #default = oracle://_____________:___________@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=______))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=_____________)))
76
+ #default = oracle+oracledb://_____________:___________@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=_________)(PORT=______))(ADDRESS=(PROTOCOL=TCP)(HOST=_________)(PORT=_____))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=__________)))
77
+ #default = oracle+oracledb://_____________:___________@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=______))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=_____________)))
78
78
  #schema=atlas_rucio # only for cern oracle
79
79
  #default = mysql+pymysql://rucio:rucio@localhost/rucio
80
80
  #default = postgresql+psycopg://rucio:rucio@localhost/rucio
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- ALEMBIC_REVISION = '30d5206e9cad' # the current alembic head revision
15
+ ALEMBIC_REVISION = 'a62db546a1f1' # the current alembic head revision
@@ -20,12 +20,10 @@ import os
20
20
  import signal
21
21
  import sys
22
22
  import time
23
- import traceback
24
23
  import unittest
25
24
  import uuid
26
25
  from copy import deepcopy
27
26
  from datetime import datetime
28
- from logging import DEBUG
29
27
  from typing import TYPE_CHECKING, Optional
30
28
 
31
29
  from rich.console import Console
@@ -45,13 +43,13 @@ from rucio.common.client import detect_client_location
45
43
  from rucio.common.config import config_get, config_get_float
46
44
  from rucio.common.constants import ReplicaState
47
45
  from rucio.common.exception import (
48
- DIDFilterSyntaxError,
49
- DuplicateCriteriaInDIDFilter,
50
46
  DuplicateRule,
51
47
  InputValidationError,
52
48
  InvalidObject,
53
49
  InvalidType,
50
+ RSENotFound,
54
51
  RucioException,
52
+ ScopeNotFound,
55
53
  UnsupportedOperation,
56
54
  )
57
55
  from rucio.common.extra import import_extras
@@ -109,8 +107,7 @@ def ping(args, client, logger, console, spinner):
109
107
  if server_info:
110
108
  print(server_info['version'])
111
109
  return SUCCESS
112
- logger.error('Ping failed')
113
- return FAILURE
110
+ raise RucioException('Ping failed')
114
111
 
115
112
 
116
113
  @exception_handler
@@ -232,11 +229,9 @@ def list_file_replicas(args, client, logger, console, spinner):
232
229
  table_data = []
233
230
  dids = []
234
231
  if args.missing and not args.rses:
235
- print('Cannot use --missing without specifying a RSE')
236
- return FAILURE
232
+ raise InputValidationError('Cannot use --missing without specifying a RSE')
237
233
  if args.link and ':' not in args.link:
238
- print('The substitution parameter must equal --link="/pfn/dir:/dst/dir"')
239
- return FAILURE
234
+ raise ValueError('The substitution parameter must equal --link="/pfn/dir:/dst/dir"')
240
235
 
241
236
  if cli_config == 'rich':
242
237
  spinner.update(status='Fetching file replicas')
@@ -398,14 +393,13 @@ def attach(args, client, logger, console, spinner):
398
393
 
399
394
  if args.fromfile:
400
395
  if len(dids) > 1:
401
- logger.error("If --fromfile option is active, only one file is supported. The file should contain a list of dids, one per line.")
402
- return FAILURE
396
+ raise ValueError('If --fromfile option is active, only one file is supported. The file should contain a list of dids, one per line.')
403
397
  try:
404
398
  f = open(dids[0], 'r')
405
399
  dids = [did.rstrip() for did in f.readlines()]
406
- except OSError:
400
+ except OSError as error:
407
401
  logger.error("Can't open file '" + dids[0] + "'.")
408
- return FAILURE
402
+ raise OSError from error
409
403
 
410
404
  dids = [{'scope': get_scope(did, client)[0], 'name': get_scope(did, client)[1]} for did in dids]
411
405
  if len(dids) <= limit:
@@ -468,35 +462,16 @@ def list_dids(args, client, logger, console, spinner):
468
462
  name = '*'
469
463
 
470
464
  if scope not in client.list_scopes():
471
- logger.error('Scope not found.')
472
- return FAILURE
465
+ raise ScopeNotFound
473
466
 
474
467
  if args.recursive and '*' in name:
475
- logger.error('Option recursive cannot be used with wildcards.')
476
- return FAILURE
468
+ raise InputValidationError('Option recursive cannot be used with wildcards.')
477
469
  else:
478
470
  if filters:
479
471
  if ('name' in filters) and (name != '*'):
480
- logger.error('Must have a wildcard in did name if filtering by name.')
481
- return FAILURE
472
+ raise ValueError('Must have a wildcard in did name if filtering by name.')
482
473
 
483
- try:
484
- filters, type_ = parse_did_filter_from_string_fe(args.filter, name)
485
- except InvalidType as error:
486
- logger.error(error)
487
- return FAILURE
488
- except DuplicateCriteriaInDIDFilter as error:
489
- logger.error(error)
490
- return FAILURE
491
- except DIDFilterSyntaxError as error:
492
- logger.error(error)
493
- return FAILURE
494
- except ValueError as error:
495
- logger.error(error)
496
- return FAILURE
497
- except Exception as e:
498
- logger.error(e)
499
- return FAILURE
474
+ filters, type_ = parse_did_filter_from_string_fe(args.filter, name)
500
475
 
501
476
  if cli_config == 'rich':
502
477
  spinner.update(status='Fetching DIDs')
@@ -531,8 +506,7 @@ def list_dids_extended(args, client, logger, console, spinner):
531
506
 
532
507
  List the data identifiers for a given scope (DEPRECATED).
533
508
  """
534
- logger.error("This command has been deprecated. Please use list_dids instead.")
535
- return FAILURE
509
+ raise UnsupportedOperation('This command has been deprecated. Please use list_dids instead.')
536
510
 
537
511
 
538
512
  @exception_handler
@@ -542,15 +516,17 @@ def list_scopes(args, client, logger, console, spinner):
542
516
 
543
517
  List scopes.
544
518
  """
545
- # For the moment..
546
-
547
- if cli_config == 'rich':
519
+ if (cli_config == 'rich') or (not args.csv):
548
520
  spinner.update(status='Fetching scopes')
549
521
  spinner.start()
550
522
 
551
- scopes = client.list_scopes()
552
- if cli_config == 'rich':
553
- table = generate_table([[scope] for scope in sorted(scopes)], headers=['SCOPE'], col_alignments=['left'])
523
+ if args.account:
524
+ scopes = client.list_scopes_for_account(args.account)
525
+ else:
526
+ scopes = client.list_scopes()
527
+ if (cli_config == 'rich') and (not args.csv):
528
+ scopes = [[scope] for scope in sorted(scopes)]
529
+ table = generate_table(scopes, headers=['SCOPE'], col_alignments=['left'])
554
530
  spinner.stop()
555
531
  print_output(table, console=console, no_pager=args.no_pager)
556
532
  else:
@@ -813,8 +789,7 @@ def list_parent_dids(args, client, logger, console, spinner):
813
789
  else:
814
790
  print(tabulate(table_data, tablefmt=tablefmt, headers=['SCOPE:NAME', '[DID TYPE]']))
815
791
  else:
816
- print('At least one option has to be given. Use -h to list the options.')
817
- return FAILURE
792
+ raise InputValidationError('At least one option has to be given. Use -h to list the options.')
818
793
  return SUCCESS
819
794
 
820
795
 
@@ -931,13 +906,11 @@ def upload(args, client, logger, console, spinner):
931
906
  Upload files into Rucio
932
907
  """
933
908
  if args.lifetime and args.expiration_date:
934
- logger.error("--lifetime and --expiration-date cannot be specified at the same time.")
935
- return FAILURE
909
+ raise InputValidationError("--lifetime and --expiration-date cannot be specified at the same time.")
936
910
  elif args.expiration_date:
937
911
  expiration_date = datetime.strptime(args.expiration_date, "%Y-%m-%d-%H:%M:%S")
938
912
  if expiration_date < datetime.utcnow():
939
- logger.error("The specified expiration date should be in the future!")
940
- return FAILURE
913
+ raise ValueError("The specified expiration date should be in the future!")
941
914
  args.lifetime = (expiration_date - datetime.utcnow()).total_seconds()
942
915
 
943
916
  dsscope = None
@@ -1023,23 +996,18 @@ def download(args, client, logger, console, spinner):
1023
996
  """
1024
997
  # Input validation
1025
998
  if not args.dids and not args.filter and not args.metalink_file:
1026
- logger.error('At least one did is mandatory')
1027
- return FAILURE
999
+ raise InputValidationError('At least one did is mandatory')
1028
1000
  elif not args.dids and args.filter and not args.scope:
1029
- logger.error('The argument scope is mandatory')
1030
- return FAILURE
1001
+ raise InputValidationError('The argument scope is mandatory')
1031
1002
 
1032
1003
  if args.filter and args.metalink_file:
1033
- logger.error('Arguments filter and metalink cannot be used together.')
1034
- return FAILURE
1004
+ raise InputValidationError('Arguments filter and metalink cannot be used together.')
1035
1005
 
1036
1006
  if args.dids and args.metalink_file:
1037
- logger.error('Arguments dids and metalink cannot be used together.')
1038
- return FAILURE
1007
+ raise InputValidationError('Arguments dids and metalink cannot be used together.')
1039
1008
 
1040
1009
  if args.ignore_checksum and args.check_local_with_filesize_only:
1041
- logger.error('Arguments ignore-checksum and check-local-with-filesize-only cannot be used together.')
1042
- return FAILURE
1010
+ raise InputValidationError('Arguments ignore-checksum and check-local-with-filesize-only cannot be used together.')
1043
1011
 
1044
1012
  trace_pattern = {}
1045
1013
 
@@ -1083,16 +1051,12 @@ def download(args, client, logger, console, spinner):
1083
1051
  filters, type_ = parse_did_filter_from_string(args.filter)
1084
1052
  if args.scope:
1085
1053
  filters['scope'] = args.scope
1086
- except InvalidType as error:
1054
+ except (InvalidType, ValueError) as error:
1087
1055
  logger.error(error)
1088
- return FAILURE
1089
- except ValueError as error:
1090
- logger.error(error)
1091
- return FAILURE
1056
+ raise error
1092
1057
  except Exception as error:
1093
- logger.error(error)
1094
1058
  logger.error("Invalid Filter. Filter must be 'key=value', 'key>=value', 'key>value', 'key<=value', 'key<value'")
1095
- return FAILURE
1059
+ raise error
1096
1060
  item_defaults['filters'] = filters
1097
1061
 
1098
1062
  if not args.pfn:
@@ -1148,8 +1112,7 @@ def download(args, client, logger, console, spinner):
1148
1112
 
1149
1113
  download_rse = _get_rse_for_pfn(replicas, args.pfn)
1150
1114
  if download_rse is None:
1151
- logger.error("Could not find RSE for pfn %s", args.pfn)
1152
- return FAILURE
1115
+ raise RSENotFound("Could not find RSE for pfn %s" % args.pfn)
1153
1116
  else:
1154
1117
  item_defaults['rse'] = download_rse
1155
1118
 
@@ -1354,8 +1317,7 @@ def delete_rule(args, client, logger, console, spinner):
1354
1317
  except ValueError:
1355
1318
  # Otherwise, trying to extract the scope, name from args.rule_id
1356
1319
  if not args.rses:
1357
- logger.error('A RSE expression must be specified if you do not provide a rule_id but a DID')
1358
- return FAILURE
1320
+ raise InputValidationError('A RSE expression must be specified if you do not provide a rule_id but a DID')
1359
1321
  scope, name = get_scope(args.rule_id, client)
1360
1322
  rules = client.list_did_rules(scope=scope, name=name)
1361
1323
  if args.rule_account is None:
@@ -1372,8 +1334,7 @@ def delete_rule(args, client, logger, console, spinner):
1372
1334
  client.delete_replication_rule(rule_id=rule['id'], purge_replicas=args.purge_replicas)
1373
1335
  deletion_success = True
1374
1336
  if not deletion_success:
1375
- logger.error('No replication rule was deleted from the DID')
1376
- return FAILURE
1337
+ raise RucioException('No replication rule was deleted from the DID')
1377
1338
  return SUCCESS
1378
1339
 
1379
1340
 
@@ -1394,8 +1355,8 @@ def update_rule(args, client, logger, console, spinner):
1394
1355
  elif args.locked.title() == "False":
1395
1356
  options['locked'] = False
1396
1357
  else:
1397
- logger.error('Locked must be True or False')
1398
- return FAILURE
1358
+ raise InputValidationError('Locked must be True or False')
1359
+
1399
1360
  if args.comment:
1400
1361
  options['comment'] = args.comment
1401
1362
  if args.rule_account:
@@ -1410,8 +1371,7 @@ def update_rule(args, client, logger, console, spinner):
1410
1371
  options['source_replica_expression'] = None if args.source_replica_expression.lower() == 'none' else args.source_replica_expression
1411
1372
  if args.cancel_requests:
1412
1373
  if 'state' not in options:
1413
- logger.error('--stuck or --suspend must be specified when running --cancel-requests')
1414
- return FAILURE
1374
+ raise InputValidationError('--stuck or --suspend must be specified when running --cancel-requests')
1415
1375
  options['cancel_requests'] = True
1416
1376
  if args.priority:
1417
1377
  options['priority'] = int(args.priority)
@@ -1578,12 +1538,11 @@ def list_rules(args, client, logger, console, spinner):
1578
1538
  elif args.rule_account:
1579
1539
  rules = client.list_account_rules(account=args.rule_account)
1580
1540
  elif args.subscription:
1581
- account = args.subscription[0]
1582
- name = args.subscription[1]
1541
+ account = args.rule_account if args.rule_account else client.account
1542
+ name = args.subscription
1583
1543
  rules = client.list_subscription_rules(account=account, name=name)
1584
1544
  else:
1585
- print('At least one option has to be given. Use -h to list the options.')
1586
- return FAILURE
1545
+ raise InputValidationError('At least one option has to be given. Use -h to list the options.')
1587
1546
  if args.csv:
1588
1547
  for rule in rules:
1589
1548
  print(rule['id'],
@@ -1990,21 +1949,18 @@ def add_lifetime_exception(args, client, logger, console, spinner):
1990
1949
  """
1991
1950
 
1992
1951
  if not args.reason:
1993
- logger.error('reason for the extension is mandatory')
1994
- return FAILURE
1952
+ raise InputValidationError('reason for the extension is mandatory')
1995
1953
  reason = args.reason
1996
1954
  if not args.expiration:
1997
- logger.error('expiration is mandatory')
1998
- return FAILURE
1955
+ raise InputValidationError('expiration is mandatory')
1999
1956
  try:
2000
1957
  expiration = datetime.strptime(args.expiration, "%Y-%m-%d")
2001
1958
  except Exception as err:
2002
- logger.error(err)
2003
- return FAILURE
1959
+ msg = f'Cannot parse expiration date: {err}'
1960
+ raise ValueError(msg)
2004
1961
 
2005
1962
  if not args.inputfile:
2006
- logger.error('inputfile is mandatory')
2007
- return FAILURE
1963
+ raise InputValidationError('inputfile is mandatory')
2008
1964
  with open(args.inputfile) as infile:
2009
1965
  # Deduplicate the content of the input file and ignore empty lines.
2010
1966
  dids = set(did for line in infile if (did := line.strip()))
@@ -2067,22 +2023,8 @@ def add_lifetime_exception(args, client, logger, console, spinner):
2067
2023
  if not datasets:
2068
2024
  logger.error('Nothing to submit')
2069
2025
  return SUCCESS
2070
- try:
2071
- client.add_exception(dids=datasets, account=client.account, pattern='', comments=reason, expires_at=expiration)
2072
- except UnsupportedOperation as err:
2073
- logger.error(err)
2074
- return FAILURE
2075
- except Exception:
2076
- error_message = 'Failure to submit exception. Please retry.'
2077
- if cli_config == 'rich':
2078
- if logger.level == DEBUG:
2079
- logger.exception(error_message)
2080
- else:
2081
- logger.error(error_message)
2082
- else:
2083
- logger.error(error_message)
2084
- logger.debug(traceback.format_exc())
2085
- return FAILURE
2026
+
2027
+ client.add_exception(dids=datasets, account=client.account, pattern='', comments=reason, expires_at=expiration)
2086
2028
 
2087
2029
  logger.info('Exception successfully submitted. Summary below:')
2088
2030
  for key, data in error_summary.items():
@@ -2499,6 +2441,8 @@ You can filter by key/value, e.g.::
2499
2441
  ''')
2500
2442
 
2501
2443
  scope_list_parser.set_defaults(function=list_scopes)
2444
+ scope_list_parser.add_argument("--csv", action="store_true", default=False, help="Comma Separated Value output.")
2445
+ scope_list_parser.add_argument('--account', help='Filter scopes by account')
2502
2446
 
2503
2447
  # The close command
2504
2448
  close_parser = subparsers.add_parser('close', help='Close a dataset or container.')
@@ -2699,7 +2643,7 @@ You can filter by account::
2699
2643
  list_rules_parser.add_argument('--csv', dest='csv', action='store_true', default=False, help='Comma Separated Value output')
2700
2644
  list_rules_parser.add_argument('--file', dest='file', action='store', help='List associated rules of an affected file')
2701
2645
  list_rules_parser.add_argument('--account', dest='rule_account', action='store', help='List by account')
2702
- list_rules_parser.add_argument('--subscription', dest='subscription', action='store', help='List by account and subscription name', metavar=('ACCOUNT', 'SUBSCRIPTION'), nargs=2)
2646
+ list_rules_parser.add_argument('--subscription', dest='subscription', action='store', help='List by subscription name')
2703
2647
 
2704
2648
  # The list_rules_history command
2705
2649
  list_rules_history_parser = subparsers.add_parser('list-rules-history', help='List replication rules history for a DID.')
@@ -37,8 +37,11 @@ from rucio.cli.utils import exception_handler, get_client, setup_gfal2_logger, s
37
37
  from rucio.client.richclient import MAX_TRACEBACK_WIDTH, MIN_CONSOLE_WIDTH, CLITheme, generate_table, get_cli_config, get_pager, print_output, setup_rich_logger
38
38
  from rucio.common.constants import RseAttr
39
39
  from rucio.common.exception import (
40
+ InputValidationError,
41
+ InvalidObject,
40
42
  ReplicaNotFound,
41
43
  RSEOperationNotSupported,
44
+ RucioException,
42
45
  )
43
46
  from rucio.common.extra import import_extras
44
47
  from rucio.common.utils import StoreAndDeprecateWarningAction, chunks, clean_pfns, construct_non_deterministic_pfn, extract_scope, get_bytes_value_from_string, parse_response, render_json, setup_logger, sizefmt
@@ -222,8 +225,10 @@ def set_limits(args, client, logger, console, spinner):
222
225
  try:
223
226
  byte_limit = int(limit_input)
224
227
  except ValueError:
225
- logger.error('The limit could not be set. Either you misspelled infinity or your input could not be converted to integer or you used a wrong pattern. Please use a format like 10GB with B,KB,MB,GB,TB,PB as units (not case sensitive)')
226
- return FAILURE
228
+ msg = f'\
229
+ The limit could not be set. Either you misspelled infinity or your input ({args.bytes}) could not be converted to integer or you used a wrong pattern. \
230
+ Please use a format like 10GB with B,KB,MB,GB,TB,PB as units (not case sensitive)'
231
+ raise InputValidationError(msg)
227
232
 
228
233
  client.set_account_limit(account=args.account, rse=args.rse, bytes_=byte_limit, locality=locality)
229
234
  print('Set account limit for account %s on RSE %s: %s' % (args.account, args.rse, sizefmt(byte_limit, True)))
@@ -266,12 +271,10 @@ def identity_add(args, client, logger, console, spinner):
266
271
 
267
272
  """
268
273
  if args.email == "":
269
- logger.error('Error: --email argument can\'t be an empty string. Failed to grant an identity access to an account')
270
- return FAILURE
274
+ raise InputValidationError('Error: --email argument can\'t be an empty string. Failed to grant an identity access to an account')
271
275
 
272
276
  if args.authtype == 'USERPASS' and not args.password:
273
- logger.error('missing --password argument')
274
- return FAILURE
277
+ raise InputValidationError('Missing --password argument')
275
278
 
276
279
  client.add_identity(account=args.account, identity=args.identity, authtype=args.authtype, email=args.email, password=args.password)
277
280
  print('Added new identity to account: %s-%s' % (args.identity, args.account))
@@ -634,8 +637,7 @@ def add_protocol_rse(args, client, logger, console, spinner):
634
637
  if args.ext_attr_json:
635
638
  proto['extended_attributes'] = args.ext_attr_json
636
639
  if proto['scheme'] == 'srm' and not args.web_service_path:
637
- print('Error: space-token and web-service-path must be provided for SRM endpoints.')
638
- return FAILURE
640
+ raise InputValidationError('Error: space-token and web-service-path must be provided for SRM endpoints.')
639
641
  if args.space_token:
640
642
  proto['extended_attributes']['space_token'] = args.space_token
641
643
  if args.web_service_path:
@@ -764,7 +766,7 @@ def list_scopes(args, client, logger, console, spinner):
764
766
  List scopes.
765
767
 
766
768
  """
767
- if cli_config == 'rich':
769
+ if (cli_config == 'rich') and (not args.csv):
768
770
  spinner.update(status='Fetching scopes')
769
771
  spinner.start()
770
772
 
@@ -772,15 +774,14 @@ def list_scopes(args, client, logger, console, spinner):
772
774
  scopes = client.list_scopes_for_account(args.account)
773
775
  else:
774
776
  scopes = client.list_scopes()
775
- if cli_config == 'rich':
776
- scopes = [[scope] for scope in sorted(scopes) if 'mock' not in scope]
777
+ if (cli_config == 'rich') and (not args.csv):
778
+ scopes = [[scope] for scope in sorted(scopes)]
777
779
  table = generate_table(scopes, headers=['SCOPE'], col_alignments=['left'])
778
780
  spinner.stop()
779
781
  print_output(table, console=console, no_pager=args.no_pager)
780
782
  else:
781
783
  for scope in scopes:
782
- if 'mock' not in scope:
783
- print(scope)
784
+ print(scope)
784
785
  return SUCCESS
785
786
 
786
787
 
@@ -1042,8 +1043,7 @@ def __declare_bad_file_replicas_by_lfns(args: object, client) -> object:
1042
1043
  Declare a list of bad replicas using RSE name, scope and list of LFNs.
1043
1044
  """
1044
1045
  if not args.scope or not args.rse:
1045
- print("--lfns requires using --rse and --scope")
1046
- return FAILURE
1046
+ raise InputValidationError("--lfns requires using --rse and --scope")
1047
1047
  reason = args.reason
1048
1048
  scope = args.scope
1049
1049
  rse = args.rse
@@ -1094,8 +1094,8 @@ def declare_bad_file_replicas(args, client, logger, console, spinner):
1094
1094
  scope, name = get_scope(bad_file, client)
1095
1095
  did_info = client.get_did(scope, name)
1096
1096
  if did_info['type'].upper() != 'FILE' and not args.allow_collection:
1097
- print('DID %s:%s is a collection and --allow-collection was not specified.' % (scope, name))
1098
- return FAILURE
1097
+ msg = f'DID {scope}:{name} is a collection and --allow-collection was not specified.'
1098
+ raise InputValidationError(msg)
1099
1099
  replicas = [replica for rep in client.list_replicas([{'scope': scope, 'name': name}])
1100
1100
  for replica in list(rep['pfns'].keys())]
1101
1101
  bad_files_pfns.extend(replicas)
@@ -1173,15 +1173,15 @@ def declare_temporary_unavailable_replicas(args, client, logger, console, spinne
1173
1173
  for line in infile:
1174
1174
  bad_file = line.rstrip('\n')
1175
1175
  if '://' not in bad_file:
1176
- print('%s is not a valid PFN. Aborting', bad_file)
1177
- return FAILURE
1176
+ msg = f'{bad_file} is not a valid PFN. Aborting'
1177
+ raise InvalidObject(msg)
1178
1178
  if bad_file != '':
1179
1179
  bad_files.append(bad_file)
1180
1180
  else:
1181
1181
  bad_files = args.listbadfiles
1182
1182
 
1183
1183
  if args.duration is None:
1184
- raise ValueError("Duration should have been set, something went wrong!")
1184
+ raise InputValidationError("Duration should have been set, something went wrong!")
1185
1185
 
1186
1186
  expiration_date = (datetime.datetime.utcnow() + datetime.timedelta(seconds=args.duration)).isoformat()
1187
1187
 
@@ -1246,8 +1246,7 @@ def list_pfns(args, client, logger, console, spinner):
1246
1246
  path if not path.startswith('/') else path[1:]])
1247
1247
  print(pfn)
1248
1248
  else:
1249
- logger.error('Unexpected error')
1250
- return FAILURE
1249
+ raise RucioException
1251
1250
  else:
1252
1251
  print(result)
1253
1252
  return SUCCESS
@@ -1279,7 +1278,7 @@ def import_data(args, client, logger, console, spinner):
1279
1278
  else:
1280
1279
  print('There was problem with decoding your file.')
1281
1280
  print(error)
1282
- return FAILURE
1281
+ raise ValueError from error
1283
1282
  except OSError as error:
1284
1283
  if cli_config == 'rich':
1285
1284
  spinner.stop()
@@ -1288,7 +1287,7 @@ def import_data(args, client, logger, console, spinner):
1288
1287
  else:
1289
1288
  print('There was a problem with reading your file.')
1290
1289
  print(error)
1291
- return FAILURE
1290
+ raise OSError from error
1292
1291
 
1293
1292
  if data:
1294
1293
  client.import_data(data)
@@ -1304,7 +1303,7 @@ def import_data(args, client, logger, console, spinner):
1304
1303
  print_output('Nothing to import.', console=console, no_pager=True)
1305
1304
  else:
1306
1305
  print('Nothing to import.')
1307
- return FAILURE
1306
+ raise ValueError
1308
1307
 
1309
1308
 
1310
1309
  @exception_handler
@@ -1342,7 +1341,7 @@ def export_data(args, client, logger, console, spinner):
1342
1341
  else:
1343
1342
  print('There was a problem with reading your file.')
1344
1343
  print(error)
1345
- return FAILURE
1344
+ raise OSError from error
1346
1345
 
1347
1346
 
1348
1347
  @exception_handler
@@ -2131,6 +2130,7 @@ def get_parser():
2131
2130
  '\n')
2132
2131
  list_scope_parser.set_defaults(which='list_scopes')
2133
2132
  list_scope_parser.add_argument('--account', dest='account', action='store', help='Account name')
2133
+ list_scope_parser.add_argument('--csv', action='store_true', help='Output a list of scopes as a csv')
2134
2134
 
2135
2135
  # The config subparser
2136
2136
  config_parser = subparsers.add_parser('config',
@@ -76,6 +76,7 @@ class LazyGroup(click.Group):
76
76
  "scope": "rucio.cli.scope.scope",
77
77
  "subscription": "rucio.cli.subscription.subscription",
78
78
  "upload": "rucio.cli.upload.upload_command",
79
+ "opendata": "rucio.cli.opendata.opendata",
79
80
  },
80
81
  context_settings={"help_option_names": ["-h", "--help"]}
81
82
  ) # TODO: Implement https://click.palletsprojects.com/en/stable/options/#dynamic-defaults-for-prompts for args from config or os
@@ -117,7 +117,7 @@ def content():
117
117
 
118
118
 
119
119
  @content.command("history")
120
- @click.argument("dids", nargs=-1)
120
+ @click.argument("dids", nargs=-1, required=True)
121
121
  @click.pass_context
122
122
  def content_history(ctx, dids):
123
123
  """List the content history of a collection-type DID"""
@@ -146,7 +146,7 @@ def content_remove(ctx, dids, from_did):
146
146
 
147
147
 
148
148
  @content.command("list")
149
- @click.argument("dids", nargs=-1)
149
+ @click.argument("dids", nargs=-1, required=True)
150
150
  @click.option("--short", is_flag=True, default=False, help="Just dump the list of DIDs.")
151
151
  @click.pass_context
152
152
  def content_list_(ctx, dids, short):