Coverage for ezdag/format.py: 90.2%
41 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-29 15:59 -0700
« prev ^ index » next coverage.py v7.6.4, created at 2024-10-29 15:59 -0700
1# Copyright (C) 2023 Cardiff University
2#
3# This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
4# If a copy of the MPL was not distributed with this file, You can obtain one at
5# <https://mozilla.org/MPL/2.0/>.
6#
7# SPDX-License-Identifier: MPL-2.0
9"""HTCondor submit command formatting utilities."""
11from __future__ import annotations
13from typing import Any, Callable, Iterable
16def _separated_factory(delimiter: str) -> Callable:
17 """Factory function to define a delimiter-joiner function."""
19 def _joiner(value: Iterable[Any]) -> str:
20 return delimiter.join(map(str, value))
22 return _joiner
25_comma_separated = _separated_factory(", ")
26_space_separated = _separated_factory(" ")
27_semicolon_separated = _separated_factory("; ")
30def _format_argument(arg: str) -> str:
31 """Format a single command argument for inclusion in the condor_submit
32 ``arguments`` command.
34 According to the condor_submit _New Syntax_ description.
35 """
36 return "'{}'".format(
37 arg.replace('"', '""').replace("'", "''"),
38 )
41def _format_arguments(value: Iterable[str]) -> str:
42 """Format an iterable of command arguments for inclusion in the condor_submit
43 ``arguments`` command.
45 According to the condor_submit _New Syntax_ description.
46 """
47 return '"{}"'.format(" ".join(map(_format_argument, value)))
50def _format_environment(value: dict[str, Any]) -> str:
51 """Format a `dict` of environment settings for the condor_submit
52 ``environment`` command.
53 """
54 return '"{}"'.format(" ".join(f"{key}='{value}'" for key, value in value.items()))
57def _format_classad_item(value: Any) -> str:
58 """Format a single classad expression item."""
59 return f"({value})"
62def _format_classad_boolean_expression(
63 value: Iterable[Any],
64 operator: str = "&&",
65) -> str:
66 """Format an iterable of requirements expressions as a ClassAd Boolean
67 Expression for the condor_submit ``requirements`` command, or similar,
68 using the relevant operator.
69 """
70 return " {operator} ".join(map(_format_classad_item, value))
73def _semicolon_separated_pairs(value: dict[str, Any]) -> str:
74 """Format a dict as a semicolon-separated list of key, value pairs
75 for the condor_submit ``transfer_output_remaps`` command, or similar.
76 """
78 def _format_item(key):
79 val = value[key]
80 if val is None:
81 return key
82 return f"{key}={val}"
84 return '"{}"'.format("; ".join(map(_format_item, value)))
87DICT_FORMATTERS: dict[str, Callable] = {
88 "environment": _format_environment,
89 "transfer_output_remaps": _semicolon_separated_pairs,
90 "transfer_plugins": _semicolon_separated_pairs,
91}
92LIST_FORMATTERS: dict[str, Callable] = {
93 "arguments": _format_arguments,
94 "getenv": _comma_separated,
95 "requirements": _format_classad_boolean_expression,
96 "transfer_checkpoint_files": _comma_separated,
97 "transfer_input_files": _comma_separated,
98 "transfer_output_files": _comma_separated,
99}
102def _format_condor_command_value(
103 command: str,
104 value: Any,
105) -> str:
106 """Format a condor submit command value."""
107 if isinstance(value, dict) and command.lower() in DICT_FORMATTERS:
108 return DICT_FORMATTERS[command.lower()](value)
109 if isinstance(value, (list, tuple)) and command.lower() in LIST_FORMATTERS:
110 return LIST_FORMATTERS[command.lower()](value)
112 # special cases for dynamically-named commands
113 if isinstance(value, (list, tuple)) and (
114 "_oauth_permissions" in command or "_oauth_resource" in command
115 ):
116 return _space_separated(value)
118 return str(value)
121def format_submit_description(desc: dict[str, Any]) -> dict[str, str]:
122 """Format a dictionary of submit description options.
124 This method converts 'arbitrary' Pythonic input types for the values of
125 condor submit description options into a string for inclusion in a
126 condor submit file.
127 """
128 out = {}
129 for key, value in desc.items():
130 out[key] = _format_condor_command_value(key, value)
131 return out