rucio-clients 35.7.0__py3-none-any.whl → 37.0.0rc2__py3-none-any.whl

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 (86) hide show
  1. rucio/alembicrevision.py +1 -1
  2. rucio/cli/__init__.py +14 -0
  3. rucio/cli/account.py +216 -0
  4. rucio/cli/bin_legacy/__init__.py +13 -0
  5. rucio_clients-35.7.0.data/scripts/rucio → rucio/cli/bin_legacy/rucio.py +769 -486
  6. rucio_clients-35.7.0.data/scripts/rucio-admin → rucio/cli/bin_legacy/rucio_admin.py +476 -423
  7. rucio/cli/command.py +272 -0
  8. rucio/cli/config.py +72 -0
  9. rucio/cli/did.py +191 -0
  10. rucio/cli/download.py +128 -0
  11. rucio/cli/lifetime_exception.py +33 -0
  12. rucio/cli/replica.py +162 -0
  13. rucio/cli/rse.py +293 -0
  14. rucio/cli/rule.py +158 -0
  15. rucio/cli/scope.py +40 -0
  16. rucio/cli/subscription.py +73 -0
  17. rucio/cli/upload.py +60 -0
  18. rucio/cli/utils.py +226 -0
  19. rucio/client/accountclient.py +0 -1
  20. rucio/client/baseclient.py +33 -24
  21. rucio/client/client.py +45 -1
  22. rucio/client/didclient.py +5 -3
  23. rucio/client/downloadclient.py +6 -8
  24. rucio/client/replicaclient.py +0 -2
  25. rucio/client/richclient.py +317 -0
  26. rucio/client/rseclient.py +4 -4
  27. rucio/client/uploadclient.py +26 -12
  28. rucio/common/bittorrent.py +234 -0
  29. rucio/common/cache.py +66 -29
  30. rucio/common/checksum.py +168 -0
  31. rucio/common/client.py +122 -0
  32. rucio/common/config.py +22 -35
  33. rucio/common/constants.py +61 -3
  34. rucio/common/didtype.py +72 -24
  35. rucio/common/exception.py +65 -8
  36. rucio/common/extra.py +5 -10
  37. rucio/common/logging.py +13 -13
  38. rucio/common/pcache.py +8 -7
  39. rucio/common/plugins.py +59 -27
  40. rucio/common/policy.py +12 -3
  41. rucio/common/schema/__init__.py +84 -34
  42. rucio/common/schema/generic.py +0 -17
  43. rucio/common/schema/generic_multi_vo.py +0 -17
  44. rucio/common/stomp_utils.py +383 -119
  45. rucio/common/test_rucio_server.py +12 -6
  46. rucio/common/types.py +132 -52
  47. rucio/common/utils.py +93 -643
  48. rucio/rse/__init__.py +3 -3
  49. rucio/rse/protocols/bittorrent.py +11 -1
  50. rucio/rse/protocols/cache.py +0 -11
  51. rucio/rse/protocols/dummy.py +0 -11
  52. rucio/rse/protocols/gfal.py +14 -9
  53. rucio/rse/protocols/globus.py +1 -1
  54. rucio/rse/protocols/http_cache.py +1 -1
  55. rucio/rse/protocols/posix.py +2 -2
  56. rucio/rse/protocols/protocol.py +84 -317
  57. rucio/rse/protocols/rclone.py +2 -1
  58. rucio/rse/protocols/rfio.py +10 -1
  59. rucio/rse/protocols/ssh.py +2 -1
  60. rucio/rse/protocols/storm.py +2 -13
  61. rucio/rse/protocols/webdav.py +74 -30
  62. rucio/rse/protocols/xrootd.py +2 -1
  63. rucio/rse/rsemanager.py +170 -53
  64. rucio/rse/translation.py +260 -0
  65. rucio/vcsversion.py +4 -4
  66. rucio/version.py +7 -0
  67. {rucio_clients-35.7.0.data → rucio_clients-37.0.0rc2.data}/data/etc/rucio.cfg.atlas.client.template +3 -2
  68. {rucio_clients-35.7.0.data → rucio_clients-37.0.0rc2.data}/data/etc/rucio.cfg.template +3 -19
  69. {rucio_clients-35.7.0.data → rucio_clients-37.0.0rc2.data}/data/requirements.client.txt +11 -7
  70. rucio_clients-37.0.0rc2.data/scripts/rucio +133 -0
  71. rucio_clients-37.0.0rc2.data/scripts/rucio-admin +97 -0
  72. {rucio_clients-35.7.0.dist-info → rucio_clients-37.0.0rc2.dist-info}/METADATA +18 -14
  73. rucio_clients-37.0.0rc2.dist-info/RECORD +104 -0
  74. {rucio_clients-35.7.0.dist-info → rucio_clients-37.0.0rc2.dist-info}/licenses/AUTHORS.rst +3 -0
  75. rucio/common/schema/atlas.py +0 -413
  76. rucio/common/schema/belleii.py +0 -408
  77. rucio/common/schema/domatpc.py +0 -401
  78. rucio/common/schema/escape.py +0 -426
  79. rucio/common/schema/icecube.py +0 -406
  80. rucio/rse/protocols/gsiftp.py +0 -92
  81. rucio_clients-35.7.0.dist-info/RECORD +0 -88
  82. {rucio_clients-35.7.0.data → rucio_clients-37.0.0rc2.data}/data/etc/rse-accounts.cfg.template +0 -0
  83. {rucio_clients-35.7.0.data → rucio_clients-37.0.0rc2.data}/data/rucio_client/merge_rucio_configs.py +0 -0
  84. {rucio_clients-35.7.0.dist-info → rucio_clients-37.0.0rc2.dist-info}/WHEEL +0 -0
  85. {rucio_clients-35.7.0.dist-info → rucio_clients-37.0.0rc2.dist-info}/licenses/LICENSE +0 -0
  86. {rucio_clients-35.7.0.dist-info → rucio_clients-37.0.0rc2.dist-info}/top_level.txt +0 -0
@@ -29,11 +29,13 @@ from typing import TYPE_CHECKING, Any, Optional
29
29
 
30
30
  from rucio import version
31
31
  from rucio.client.client import Client
32
+ from rucio.common.checksum import CHECKSUM_ALGO_DICT, GLOBALLY_SUPPORTED_CHECKSUMS, PREFERRED_CHECKSUM, adler32
33
+ from rucio.common.client import detect_client_location
32
34
  from rucio.common.config import config_get
33
35
  from rucio.common.didtype import DID
34
36
  from rucio.common.exception import InputValidationError, NoFilesDownloaded, NotAllFilesDownloaded, RucioException
35
37
  from rucio.common.pcache import Pcache
36
- from rucio.common.utils import CHECKSUM_ALGO_DICT, GLOBALLY_SUPPORTED_CHECKSUMS, PREFERRED_CHECKSUM, adler32, detect_client_location, execute, extract_scope, generate_uuid, parse_replicas_from_file, parse_replicas_from_string, send_trace, sizefmt
38
+ from rucio.common.utils import execute, extract_scope, generate_uuid, parse_replicas_from_file, parse_replicas_from_string, send_trace, sizefmt
37
39
  from rucio.rse import rsemanager as rsemgr
38
40
 
39
41
  if TYPE_CHECKING:
@@ -323,8 +325,6 @@ class DownloadClient:
323
325
  :param deactivate_file_download_exceptions: Boolean, if file download exceptions shouldn't be raised
324
326
  :param sort: Select best replica by replica sorting algorithm. Available algorithms:
325
327
  ``geoip`` - based on src/dst IP topographical distance
326
- ``closeness`` - based on src/dst closeness
327
- ``dynamic`` - Rucio Dynamic Smart Sort (tm)
328
328
 
329
329
  :returns: a list of dictionaries with an entry for each file, containing the input options, the did, and the clientState
330
330
 
@@ -834,8 +834,6 @@ class DownloadClient:
834
834
  :param deactivate_file_download_exceptions: Boolean, if file download exceptions shouldn't be raised
835
835
  :param sort: Select best replica by replica sorting algorithm. Available algorithms:
836
836
  ``geoip`` - based on src/dst IP topographical distance
837
- ``closeness`` - based on src/dst closeness
838
- ``dynamic`` - Rucio Dynamic Smart Sort (tm)
839
837
 
840
838
  :returns: a list of dictionaries with an entry for each file, containing the input options, the did, and the clientState
841
839
 
@@ -1197,7 +1195,7 @@ class DownloadClient:
1197
1195
  if self.is_tape_excluded:
1198
1196
  try:
1199
1197
  tape_rses = [endp['rse'] for endp in self.client.list_rses(rse_expression='istape=true')]
1200
- except:
1198
+ except Exception:
1201
1199
  logger(logging.DEBUG, 'No tapes found.')
1202
1200
 
1203
1201
  # Matches each dereferenced DID back to a list of input items
@@ -1677,8 +1675,8 @@ class DownloadClient:
1677
1675
  num_successful = 0
1678
1676
  num_failed = 0
1679
1677
  for item in output_items:
1680
- clientState = item.get('clientState', FileDownloadState.FAILED)
1681
- if clientState in success_states:
1678
+ client_state = item.get('clientState', FileDownloadState.FAILED)
1679
+ if client_state in success_states:
1682
1680
  num_successful += 1
1683
1681
  else:
1684
1682
  num_failed += 1
@@ -143,8 +143,6 @@ class ReplicaClient(BaseClient):
143
143
  :param rse_expression: The RSE expression to restrict replicas on a set of RSEs.
144
144
  :param client_location: Client location dictionary for PFN modification {'ip', 'fqdn', 'site', 'latitude', 'longitude'}
145
145
  :param sort: Sort the replicas: ``geoip`` - based on src/dst IP topographical distance
146
- ``closeness`` - based on src/dst closeness
147
- ``dynamic`` - Rucio Dynamic Smart Sort (tm)
148
146
  :param domain: Define the domain. None is fallback to 'wan', otherwise 'wan, 'lan', or 'all'
149
147
  :param signature_lifetime: If supported, in seconds, restrict the lifetime of the signed PFN.
150
148
  :param nrandom: pick N random replicas. If the initial number of replicas is smaller than N, returns all replicas.
@@ -0,0 +1,317 @@
1
+ # Copyright European Organization for Nuclear Research (CERN) since 2012
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import logging
16
+ import pydoc
17
+ import re
18
+ import shutil
19
+ import sys
20
+ from datetime import datetime
21
+ from typing import TYPE_CHECKING, Any, Optional, Union
22
+
23
+ from rich import box
24
+ from rich.console import Console, JustifyMethod, RenderableType
25
+ from rich.logging import RichHandler
26
+ from rich.table import Table
27
+ from rich.text import Text
28
+
29
+ from rucio.common.config import config_get
30
+
31
+ if TYPE_CHECKING:
32
+ from collections.abc import Callable, Sequence
33
+
34
+ from rich.style import StyleType
35
+
36
+
37
+ MIN_CONSOLE_WIDTH = 80
38
+ MAX_TRACEBACK_WIDTH = 120 # Slightly higher than default width of rich.traceback (100).
39
+
40
+
41
+ class CLITheme:
42
+ """
43
+ Class to define styles for Rich widgets and prints in the CLI.
44
+ """
45
+ TABLE_FMT = box.SQUARE
46
+
47
+ TEXT_HIGHLIGHT = 'grey50' # Used to highlight prints between tables, e.g. get_metadata or stat.
48
+ SUBHEADER_HIGHLIGHT = 'cyan' # Used to highlight prints between tables for a section, e.g. protocols or usage per account.
49
+ SPINNER = 'dots2'
50
+ SPINNER_STYLE = 'green'
51
+
52
+ JSON_STR = 'green'
53
+ JSON_NUM = 'bold cyan'
54
+
55
+ SUCCESS_ICON = '[bold green]\u2714[/]'
56
+ FAILURE_ICON = '[bold red]\u2717[/]'
57
+
58
+ LOG_THEMES = {
59
+ 'logging.level.info': 'default',
60
+ 'logging.level.warning': 'bold yellow',
61
+ 'logging.level.debug': 'dim',
62
+ 'logging.level.critical': 'default on red',
63
+ 'repr.bool_true': 'green',
64
+ 'repr.bool_false': 'red',
65
+ 'log.time': 'turquoise4'
66
+ }
67
+
68
+ DID_TYPE = {
69
+ 'CONTAINER': 'bold dodger_blue1',
70
+ 'DATASET': 'bold orange3',
71
+ 'FILE': 'bold default',
72
+ 'COLLECTION': 'bold green',
73
+ 'ALL': 'bold magenta',
74
+ 'DERIVED': 'bold magenta'
75
+ }
76
+
77
+ BOOLEAN = {
78
+ 'True': 'green',
79
+ 'False': 'red'
80
+ }
81
+
82
+ RULE_STATE = {
83
+ 'OK': 'bold green',
84
+ 'REPLICATING': 'bold default',
85
+ 'STUCK': 'bold orange3',
86
+ 'WAITING APPROVAL': 'bold dodger_blue1',
87
+ 'INJECT': 'bold medium_purple3',
88
+ 'SUSPENDED': 'bold red'
89
+ }
90
+
91
+ RSE_TYPE = {
92
+ 'DISK': 'bold dodger_blue1',
93
+ 'TAPE': 'bold orange3',
94
+ 'UNKNOWN': 'bold'
95
+ }
96
+
97
+ SUBSCRIPTION_STATE = {
98
+ 'ACTIVE': 'bold green',
99
+ 'INACTIVE': 'bold',
100
+ 'NEW': 'bold dodger_blue1',
101
+ 'UPDATED': 'bold green',
102
+ 'BROKEN': 'bold red',
103
+ 'UNKNOWN': 'bold orange3'
104
+ }
105
+
106
+ AVAILABILITY = {
107
+ 'AVAILABLE': 'bold green',
108
+ 'DELETED': 'bold',
109
+ 'LOST': 'bold red',
110
+ 'UNKNOWN': 'bold orange3'
111
+ }
112
+
113
+ ACCOUNT_STATUS = {
114
+ 'ACTIVE': 'bold green',
115
+ 'SUSPENDED': 'bold red',
116
+ 'DELETED': 'bold'
117
+ }
118
+
119
+ REPLICA_STATE = {
120
+ 'A': 'bold green',
121
+ 'U': 'bold red',
122
+ 'C': 'bold default',
123
+ 'B': 'bold dodger_blue1',
124
+ 'D': 'bold red',
125
+ 'T': 'bold orange3'
126
+ }
127
+
128
+ ACCOUNT_TYPE = {
129
+ 'USER': 'default',
130
+ 'GROUP': 'medium_purple3',
131
+ 'SERVICE': 'yellow'
132
+ }
133
+
134
+
135
+ def setup_rich_logger(
136
+ module_name: Optional[str] = None,
137
+ logger_name: Optional[str] = None,
138
+ logger_level: Optional[int] = None,
139
+ verbose: bool = False,
140
+ console: Optional[Console] = None
141
+ ) -> logging.Logger:
142
+ """
143
+ Factory method to set logger with RichHandler.
144
+
145
+ The function is a copy of the method in rucio.common.utils setup_logger() with minor changes.
146
+
147
+ :param module_name: __name__ of the module that is calling this method
148
+ :param logger_name: name of the logger, typically name of the module.
149
+ :param logger_level: if not given, fetched from config.
150
+ :param verbose: verbose option set in bin/rucio
151
+ :param console: Rich console object
152
+ :returns: logger with RichHandler
153
+ """
154
+ # Helper method for cfg check.
155
+ def _force_cfg_log_level(cfg_option: str) -> bool:
156
+ cfg_forced_modules = config_get('logging', cfg_option, raise_exception=False, default=None, clean_cached=True, check_config_table=False)
157
+ if cfg_forced_modules and module_name is not None:
158
+ if re.match(str(cfg_forced_modules), module_name):
159
+ return True
160
+ return False
161
+
162
+ if not logger_name:
163
+ if not module_name:
164
+ logger_name = 'user'
165
+ else:
166
+ logger_name = module_name.split('.')[-1]
167
+ logger = logging.getLogger(logger_name)
168
+
169
+ # Extracting the log level.
170
+ if not logger_level:
171
+ logger_level = logging.INFO
172
+ if verbose:
173
+ logger_level = logging.DEBUG
174
+
175
+ # Overriding by the config.
176
+ cfg_levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR)
177
+ for level in cfg_levels:
178
+ cfg_opt = 'forceloglevel' + logging.getLevelName(level)
179
+ if _force_cfg_log_level(cfg_opt):
180
+ logger_level = level
181
+
182
+ logger.setLevel(logger_level)
183
+
184
+ def add_handler(logger: logging.Logger,
185
+ console: Optional[Console] = None,
186
+ verbose: bool = False) -> None:
187
+
188
+ def time_formatter(timestamp: datetime) -> Text:
189
+ return Text(f"[{timestamp.isoformat(sep=' ', timespec='milliseconds')}]")
190
+
191
+ console = console or Console()
192
+ handler = RichHandler(rich_tracebacks=True, markup=True, show_path=verbose, show_time=verbose, console=console, tracebacks_width=min(console.width, MAX_TRACEBACK_WIDTH),
193
+ tracebacks_word_wrap=True, log_time_format=time_formatter)
194
+ logger.addHandler(handler)
195
+
196
+ # Setting handler and formatter.
197
+ if not logger.handlers:
198
+ add_handler(logger, console, verbose)
199
+
200
+ return logger
201
+
202
+
203
+ def _format_value(value: Optional[Union[RenderableType, int, float, bool, datetime]] = None) -> RenderableType:
204
+ """
205
+ Formats the value based on its type for Rich Table.
206
+
207
+ A helper function to format the value to Rich RenderableType.
208
+
209
+ :param value: value to format
210
+ :returns: formatted value
211
+ """
212
+ if value is None or str(value) == 'None':
213
+ return ''
214
+ if isinstance(value, bool):
215
+ return CLITheme.BOOLEAN[str(value)]
216
+ if isinstance(value, (int, float, datetime)):
217
+ return str(value)
218
+ return value
219
+
220
+
221
+ def generate_table(
222
+ rows: 'Sequence[Sequence[Union[RenderableType, int, float, bool, datetime]]]',
223
+ headers: Optional['Sequence[RenderableType]'] = None,
224
+ row_styles: Optional['Sequence[StyleType]'] = None,
225
+ col_alignments: Optional[list[JustifyMethod]] = None,
226
+ table_format: box.Box = CLITheme.TABLE_FMT,
227
+ ) -> Table:
228
+ """
229
+ Generates a Rich Table object from given input rows.
230
+
231
+ The elements in each row can be either plain strings or Rich renderable objects.
232
+ Passing strings will display them as simple text, while using Rich objects
233
+ allows you to introduce additional structure, styling, and widgets (e.g. Text, Trees) into
234
+ the table. Strings with style markup will be rendered as styled text.
235
+
236
+ :param table_format: style of the table
237
+ :param headers: list of headers
238
+ :param rows: list of rows
239
+ :param col_alignments: list of column alignments
240
+ :param row_styles: list of row styles
241
+ :returns: a Rich Table object
242
+ """
243
+ table = Table(box=table_format, show_header=headers is not None and len(headers) > 0)
244
+ table.row_styles = row_styles or ['none', 'dim']
245
+
246
+ if len(rows) == 0:
247
+ if headers:
248
+ for header in headers:
249
+ table.add_column(header)
250
+ return table
251
+
252
+ # Auto-detect on first row, numerical values on the right.
253
+ col_alignments = col_alignments or ['right' if str(col).isnumeric() else 'left' for col in rows[0]]
254
+ headers = headers or [''] * len(rows[0])
255
+ while len(headers) > len(col_alignments):
256
+ col_alignments.append('left')
257
+
258
+ for header, alignment in zip(headers, col_alignments):
259
+ table.add_column(header, overflow='fold', justify=alignment)
260
+
261
+ for row in rows:
262
+ row = [_format_value(col) for col in row]
263
+ table.add_row(*row)
264
+ return table
265
+
266
+
267
+ def print_output(
268
+ *output: Any,
269
+ console: Console,
270
+ no_pager: bool = False
271
+ ) -> None:
272
+ """
273
+ Prints the objects using the specified Rich console object. Optionally disables the pager if specified.
274
+
275
+ The function works similarly to Rich's `console.print()` method but provides additional control over the pager feature.
276
+
277
+ :param output: objects to print to the terminal
278
+ :param console: Rich console object
279
+ :param no_pager: flag to disable the pager
280
+ """
281
+ if console.is_terminal:
282
+ if no_pager:
283
+ console.print(*output)
284
+ else:
285
+ console.width = sys.maxsize # Overwrite auto-detected console width.
286
+ console.begin_capture()
287
+ console.print(*output)
288
+ else:
289
+ console.width = sys.maxsize
290
+ console.print(*output)
291
+
292
+
293
+ def get_cli_config() -> str:
294
+ """
295
+ Returns the CLI type from the config file.
296
+
297
+ :returns: CLI type (Rich or tabulate)
298
+ """
299
+ cli_type = config_get('experimental', 'cli', raise_exception=False, default='tabulate').lower()
300
+ if cli_type not in ['rich', 'tabulate']:
301
+ cli_type = 'tabulate'
302
+ return cli_type
303
+
304
+
305
+ def get_pager() -> 'Callable[[str], None]':
306
+ """
307
+ Returns the pager function based on the terminal availability.
308
+
309
+ :returns: pager
310
+ """
311
+ default_pager = 'less'
312
+ # Attempt to use the default pager if available.
313
+ if shutil.which(default_pager) is not None:
314
+ return lambda text: pydoc.pipepager(text, f'{default_pager} -FRSXKM')
315
+
316
+ # Fall back to pydoc.pager if the default pager is not available.
317
+ return pydoc.pager
rucio/client/rseclient.py CHANGED
@@ -24,7 +24,7 @@ from rucio.common.utils import build_url
24
24
  if TYPE_CHECKING:
25
25
  from collections.abc import Iterable, Iterator
26
26
 
27
- from rucio.common.constants import RSE_SUPPORTED_PROTOCOL_DOMAINS_LITERAL, RSE_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL, SUPPORTED_PROTOCOLS_LITERAL
27
+ from rucio.common.constants import RSE_ALL_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL, RSE_SUPPORTED_PROTOCOL_DOMAINS_LITERAL, SUPPORTED_PROTOCOLS_LITERAL
28
28
 
29
29
 
30
30
  class RSEClient(BaseClient):
@@ -238,7 +238,7 @@ class RSEClient(BaseClient):
238
238
  self,
239
239
  rse: str,
240
240
  protocol_domain: "RSE_SUPPORTED_PROTOCOL_DOMAINS_LITERAL" = 'ALL',
241
- operation: Optional["RSE_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL"] = None,
241
+ operation: Optional["RSE_ALL_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL"] = None,
242
242
  default: bool = False,
243
243
  scheme: Optional['SUPPORTED_PROTOCOLS_LITERAL'] = None
244
244
  ) -> Any:
@@ -287,7 +287,7 @@ class RSEClient(BaseClient):
287
287
  rse: str,
288
288
  lfns: 'Iterable[str]',
289
289
  protocol_domain: 'RSE_SUPPORTED_PROTOCOL_DOMAINS_LITERAL' = 'ALL',
290
- operation: Optional['RSE_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL'] = None,
290
+ operation: Optional['RSE_ALL_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL'] = None,
291
291
  scheme: Optional['SUPPORTED_PROTOCOLS_LITERAL'] = None
292
292
  ) -> dict[str, str]:
293
293
  """
@@ -409,7 +409,7 @@ class RSEClient(BaseClient):
409
409
  self,
410
410
  rse: str,
411
411
  domain: 'RSE_SUPPORTED_PROTOCOL_DOMAINS_LITERAL',
412
- operation: 'RSE_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL',
412
+ operation: 'RSE_ALL_SUPPORTED_PROTOCOL_OPERATIONS_LITERAL',
413
413
  scheme_a: 'SUPPORTED_PROTOCOLS_LITERAL',
414
414
  scheme_b: 'SUPPORTED_PROTOCOLS_LITERAL'
415
415
  ) -> bool:
@@ -21,10 +21,13 @@ import os.path
21
21
  import random
22
22
  import socket
23
23
  import time
24
- from typing import TYPE_CHECKING, Any, Final, Optional, Union, cast
24
+ from typing import TYPE_CHECKING, Any, Final, Optional, cast
25
25
 
26
26
  from rucio import version
27
27
  from rucio.client.client import Client
28
+ from rucio.common.bittorrent import bittorrent_v2_merkle_sha256
29
+ from rucio.common.checksum import GLOBALLY_SUPPORTED_CHECKSUMS, adler32, md5
30
+ from rucio.common.client import detect_client_location
28
31
  from rucio.common.config import config_get, config_get_bool, config_get_int
29
32
  from rucio.common.constants import RseAttr
30
33
  from rucio.common.exception import (
@@ -42,13 +45,13 @@ from rucio.common.exception import (
42
45
  ScopeNotFound,
43
46
  ServiceUnavailable,
44
47
  )
45
- from rucio.common.utils import GLOBALLY_SUPPORTED_CHECKSUMS, adler32, bittorrent_v2_merkle_sha256, detect_client_location, execute, generate_uuid, make_valid_did, md5, retry, send_trace
48
+ from rucio.common.utils import execute, generate_uuid, make_valid_did, retry, send_trace
46
49
  from rucio.rse import rsemanager as rsemgr
47
50
 
48
51
  if TYPE_CHECKING:
49
52
  from collections.abc import Iterable, Mapping
50
53
 
51
- from rucio.common.types import AttachDict, DatasetDict, DIDStringDict, FileToUploadDict, FileToUploadWithCollectedAndDatasetInfoDict, FileToUploadWithCollectedInfoDict, LoggerFunction, PathTypeAlias, RSESettingsDict, TraceBaseDict, TraceDict
54
+ from rucio.common.types import AttachDict, DatasetDict, DIDStringDict, FileToUploadDict, FileToUploadWithCollectedAndDatasetInfoDict, FileToUploadWithCollectedInfoDict, LFNDict, LoggerFunction, PathTypeAlias, RSESettingsDict, TraceBaseDict, TraceDict
52
55
  from rucio.rse.protocols.protocol import RSEProtocol
53
56
 
54
57
 
@@ -78,6 +81,16 @@ class UploadClient:
78
81
  self.tracing = tracing
79
82
  if not self.tracing:
80
83
  logger(logging.DEBUG, 'Tracing is turned off.')
84
+ if self.client.account is None:
85
+ self.logger(logging.DEBUG, 'No account specified, querying rucio.')
86
+ try:
87
+ acc = self.client.whoami()
88
+ if acc is None:
89
+ raise InputValidationError('account not specified and rucio has no account with your identity')
90
+ self.client.account = acc['account']
91
+ except RucioException as e:
92
+ raise InputValidationError('account not specified and problem with rucio: %s' % e)
93
+ self.logger(logging.DEBUG, 'Discovered account as "%s"' % self.client.account)
81
94
  self.default_file_scope: Final[str] = 'user.' + self.client.account
82
95
  self.rses = {}
83
96
  self.rse_expressions = {}
@@ -232,7 +245,7 @@ class UploadClient:
232
245
  # if register_after_upload, file should be overwritten if it is not registered
233
246
  # otherwise if file already exists on RSE we're done
234
247
  if register_after_upload:
235
- if rsemgr.exists(rse_settings, pfn if pfn else file_did, domain=domain, scheme=force_scheme, impl=impl, auth_token=self.auth_token, vo=self.client.vo, logger=logger):
248
+ if rsemgr.exists(rse_settings, pfn if pfn else file_did, domain=domain, scheme=force_scheme, impl=impl, auth_token=self.auth_token, vo=self.client.vo, logger=logger): # type: ignore (pfn is str)
236
249
  try:
237
250
  self.client.get_did(file['did_scope'], file['did_name'])
238
251
  logger(logging.INFO, 'File already registered. Skipping upload.')
@@ -242,7 +255,7 @@ class UploadClient:
242
255
  logger(logging.INFO, 'File already exists on RSE. Previous left overs will be overwritten.')
243
256
  delete_existing = True
244
257
  elif not is_deterministic and not no_register:
245
- if rsemgr.exists(rse_settings, pfn, domain=domain, scheme=force_scheme, impl=impl, auth_token=self.auth_token, vo=self.client.vo, logger=logger):
258
+ if rsemgr.exists(rse_settings, pfn, domain=domain, scheme=force_scheme, impl=impl, auth_token=self.auth_token, vo=self.client.vo, logger=logger): # type: ignore (pfn is str)
246
259
  logger(logging.INFO, 'File already exists on RSE with given pfn. Skipping upload. Existing replica has to be removed first.')
247
260
  trace['stateReason'] = 'File already exists'
248
261
  continue
@@ -251,7 +264,7 @@ class UploadClient:
251
264
  trace['stateReason'] = 'File already exists'
252
265
  continue
253
266
  else:
254
- if rsemgr.exists(rse_settings, pfn if pfn else file_did, domain=domain, scheme=force_scheme, impl=impl, auth_token=self.auth_token, vo=self.client.vo, logger=logger):
267
+ if rsemgr.exists(rse_settings, pfn if pfn else file_did, domain=domain, scheme=force_scheme, impl=impl, auth_token=self.auth_token, vo=self.client.vo, logger=logger): # type: ignore (pfn is str)
255
268
  logger(logging.INFO, 'File already exists on RSE. Skipping upload')
256
269
  trace['stateReason'] = 'File already exists'
257
270
  continue
@@ -266,10 +279,11 @@ class UploadClient:
266
279
  protocol = protocols.pop()
267
280
  cur_scheme = protocol['scheme']
268
281
  logger(logging.INFO, 'Trying upload with %s to %s' % (cur_scheme, rse))
269
- lfn = {}
282
+ lfn: "LFNDict" = {
283
+ 'name': file['did_name'],
284
+ 'scope': file['did_scope']
285
+ }
270
286
  lfn['filename'] = basename
271
- lfn['scope'] = file['did_scope']
272
- lfn['name'] = file['did_name']
273
287
 
274
288
  for checksum_name in GLOBALLY_SUPPORTED_CHECKSUMS:
275
289
  if checksum_name in file:
@@ -612,7 +626,7 @@ class UploadClient:
612
626
  self,
613
627
  rse_settings: "RSESettingsDict",
614
628
  rse_attributes: dict[str, Any],
615
- lfn: dict[str, Union[str, int]],
629
+ lfn: "LFNDict",
616
630
  source_dir: Optional[str] = None,
617
631
  domain: str = 'wan',
618
632
  impl: Optional[str] = None,
@@ -674,7 +688,7 @@ class UploadClient:
674
688
  pfn = self.client.get_signed_url(rse_settings['rse'], sign_service, 'write', pfn)
675
689
 
676
690
  # Create a name of tmp file if renaming operation is supported
677
- pfn_tmp = cast(str, '%s.rucio.upload' % pfn if protocol_write.renaming else pfn)
691
+ pfn_tmp = cast("str", '%s.rucio.upload' % pfn if protocol_write.renaming else pfn)
678
692
  signed_read_pfn_tmp = '%s.rucio.upload' % signed_read_pfn if protocol_write.renaming else signed_read_pfn
679
693
 
680
694
  # Either DID exists or not register_after_upload
@@ -750,7 +764,7 @@ class UploadClient:
750
764
  try:
751
765
  if protocol_write.renaming:
752
766
  logger(logging.DEBUG, 'Renaming file %s to %s' % (pfn_tmp, pfn))
753
- protocol_write.rename(pfn_tmp, pfn)
767
+ protocol_write.rename(pfn_tmp, pfn) # type: ignore (pfn might be None)
754
768
  except Exception:
755
769
  raise RucioException('Unable to rename the tmp file %s.' % pfn_tmp)
756
770