Completed
Push — master ( 35f008...c3d926 )
by Marc-Alexandre
10s
created

generate_logfolder_name()   A

Complexity

Conditions 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
dl 0
loc 4
rs 10
c 1
b 0
f 0
1
from __future__ import absolute_import
2
3
import os
4
import re
5
import itertools
6
import time as t
7
from os.path import join as pjoin
8
9
import smartdispatch
10
from smartdispatch import utils
11
from smartdispatch.filelock import open_with_lock
12
from smartdispatch.argument_template import argument_templates
13
14
UID_TAG = "{UID}"
15
16
17
def generate_name_from_command(command, max_length_arg=None, max_length=None):
18
    ''' Generates name from a given command.
19
20
    Generate a name by replacing spaces in command with dashes and
21
    by trimming lengthty (as defined by max_length_arg) arguments.
22
23
    Parameters
24
    ----------
25
    command : str
26
        command from which to generate the name
27
    max_length_arg : int
28
        arguments longer than this will be trimmed keeping last characters (Default: inf)
29
    max_length : int
30
        trim name if longer than this keeping last characters (Default: inf)
31
32
    Returns
33
    -------
34
    name : str
35
        slugified name
36
    '''
37
    if max_length_arg is not None:
38
        max_length_arg = min(-max_length_arg, max_length_arg)
39
40
    return generate_logfolder_name('_'.join([utils.slugify(argvalue)[max_length_arg:] for argvalue in command.split()]), max_length)
41
42
43
def generate_logfolder_name(name, max_length=None):
44
    folder_name = t.strftime("%Y-%m-%d_%H-%M-%S_")
45
    folder_name += name
46
    return folder_name[:max_length]
47
48
49
def get_commands_from_file(fileobj):
50
    ''' Reads commands from `fileobj`.
51
52
    Parameters
53
    ----------
54
    fileobj : file
55
        opened file where to read commands from
56
57
    Returns
58
    -------
59
    commands : list of str
60
        commands read from the file
61
    '''
62
    return fileobj.read().strip().split('\n')
63
64
65
def unfold_command(command):
66
    ''' Unfolds a command into a list of unfolded commands.
67
68
    Unfolding is performed for every folded arguments (see *Arguments templates*)
69
    found in `command`. Then, resulting commands are generated using the product
70
    of every unfolded arguments.
71
72
    Parameters
73
    ----------
74
    command : list of str
75
        command to unfold
76
77
    Returns
78
    -------
79
    commands : list of str
80
        commands obtained after unfolding `command`
81
82
    Arguments template
83
    ------------------
84
    *list*: "[item1 item2 ... itemN]"
85
    *range*: "[start:end]" or "[start:end:step]"
86
    '''
87
    text = utils.encode_escaped_characters(command)
88
89
    # Build the master regex with all argument's regex
90
    regex = "(" + "|".join(["(?P<{0}>{1})".format(name, arg.regex) for name, arg in argument_templates.items()]) + ")"
91
92
    pos = 0
93
    arguments = []
94
    for match in re.finditer(regex, text):
95
        # Add already unfolded argument
96
        arguments.append([text[pos:match.start()]])
97
98
        # Unfold argument
99
        argument_template_name, matched_text = next((k, v) for k, v in match.groupdict().items() if v is not None)
100
        arguments.append(argument_templates[argument_template_name].unfold(matched_text))
101
        pos = match.end()
102
103
    arguments.append([text[pos:]])  # Add remaining unfolded arguments
104
    arguments = [map(utils.decode_escaped_characters, argvalues) for argvalues in arguments]
105
    return ["".join(argvalues) for argvalues in itertools.product(*arguments)]
106
107
108
def replace_uid_tag(commands):
109
    return [command.replace("{UID}", utils.generate_uid_from_string(command)) for command in commands]
110
111
112
def get_available_queues(cluster_name):
113
    """ Fetches all available queues on the current cluster """
114
    if cluster_name is None:
115
        return {}
116
117
    smartdispatch_dir, _ = os.path.split(smartdispatch.__file__)
118
    config_dir = pjoin(smartdispatch_dir, 'config')
119
120
    config_filename = cluster_name + ".json"
121
    config_filepath = pjoin(config_dir, config_filename)
122
123
    if not os.path.isfile(config_filepath):
124
        return {}  # Unknown cluster
125
126
    queues_infos = utils.load_dict_from_json_file(config_filepath)
127
    return queues_infos
128
129
130
def get_job_folders(path, jobname, create_if_needed=False):
131
    """ Get all folder paths for a specific job (creating them if needed). """
132
    path_job = pjoin(path, jobname)
133
    path_job_logs = pjoin(path_job, 'logs')
134
    path_job_commands = pjoin(path_job, 'commands')
135
136
    if not os.path.isdir(path_job_commands):
137
        os.makedirs(path_job_commands)
138
    if not os.path.isdir(path_job_logs):
139
        os.makedirs(path_job_logs)
140
    if not os.path.isdir(pjoin(path_job_logs, "worker")):
141
        os.makedirs(pjoin(path_job_logs, "worker"))
142
    if not os.path.isdir(pjoin(path_job_logs, "job")):
143
        os.makedirs(pjoin(path_job_logs, "job"))
144
145
    return path_job, path_job_logs, path_job_commands
146
147
148
def log_command_line(path_job, command_line):
149
    """ Logs a command line in a job folder.
150
151
    The command line is append to a file named 'command_line.log' that resides
152
    in the given job folder. The current date and time is also added along
153
    each command line logged.
154
155
    Notes
156
    -----
157
    Commands save in log file might differ from sys.argv since we want to make sure
158
    we can paste the command line as-is in the terminal. This means that the quotes
159
    symbole " and the square brackets will be escaped.
160
    """
161
    with open_with_lock(pjoin(path_job, "command_line.log"), 'a') as command_line_log:
162
        command_line_log.write(t.strftime("## %Y-%m-%d %H:%M:%S ##\n"))
163
        command_line = command_line.replace('"', r'\"')  # Make sure we can paste the command line as-is
164
        command_line = re.sub(r'(\[)([^\[\]]*\\ [^\[\]]*)(\])', r'"\1\2\3"', command_line)  # Make sure we can paste the command line as-is
165
        command_line_log.write(command_line + "\n\n")
166