hostlist-expand-collect 0.0.1

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 (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/hostlist_expand_collect.rb +246 -0
  3. metadata +44 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3196f3d9812b32c67da4b1308d5a447f7b81b721
4
+ data.tar.gz: 3db1f500ef6879ea348cdf14534e5c6304aa15be
5
+ SHA512:
6
+ metadata.gz: f366f0a776c5773c851dcd42225aedc761cf0d5333b43cf1115404e493c59b5215e009ae6b599e95fef84ac285475e7d9b4bf0c97a2faa94f65cf94d1a73fbb6
7
+ data.tar.gz: 81cd9094ae40833b4b56c2fbfe4062d2038d0c24157206fae2a7a7501536627a110293d6ef203286fe21ed195ce5a7cb90bf6c0788c4608abe9fb180960cd6e1
@@ -0,0 +1,246 @@
1
+ require 'set'
2
+
3
+ MAX_SIZE = 1000
4
+
5
+ ##
6
+ # Expand a range (e.g/ 1-10 or 14), putting a prefix before
7
+ def expand_range(prefix, r)
8
+ return ["#{prefix}#{r}"] if /^[0-9]+$/.match r
9
+
10
+ m = /^([0-9]+)-([0-9]+)$/.match r
11
+ raise 'Error! Bad hostlist (bad range)' unless m
12
+
13
+ s_low, s_high = m[1], m[2]
14
+
15
+ low = s_low.to_i
16
+ high = s_high.to_i
17
+
18
+ raise 'Error! Start > Stop' if high < low
19
+ raise 'Error! Range too large' if high - low > MAX_SIZE
20
+
21
+ result = []
22
+
23
+ (low..high).each { |v| result.push "#{prefix}%0#{s_low.length}d" % v }
24
+
25
+ result
26
+ end
27
+
28
+ ##
29
+ # Expand a rangelist (e.g "1-10,14"), putting a prefix before.
30
+ def expand_rangelist(prefix, rangelist)
31
+ result = []
32
+
33
+ rangelist.split(",").each do |r|
34
+ result.concat(expand_range(prefix, r))
35
+ end
36
+
37
+ result
38
+ end
39
+
40
+ ##
41
+ # Expand a part (e.g. "x[1-2]y[1-3][1-3] (no outer level commas)")
42
+ def expand_part(s)
43
+ # Базовый случай - пустая часть развернётся в список с ""
44
+ return [""] if s == ''
45
+
46
+ # Разбить на:
47
+ # 1) Строку префикс (может быть пустой)
48
+ # 2) Rangelist в скобках (может быть пропущен)
49
+ # 3) Остальное
50
+
51
+ m = /([^,\[]*)(\[[^\]]*\])?(.*)/.match s
52
+
53
+ prefix, rangelist, rest = m[1], m[2], m[3]
54
+
55
+ # Развернём остаток
56
+ rest_expanded = expand_part rest
57
+
58
+ # Развернём собственную часть
59
+ unless rangelist
60
+ us_expanded = [prefix]
61
+ else
62
+ us_expanded = expand_rangelist(prefix, rangelist[1..-2])
63
+ end
64
+
65
+ # Комбинируем список со списком
66
+ if us_expanded.length * rest_expanded.length > MAX_SIZE
67
+ raise 'Error! Bad hostlist (results too large)'
68
+ end
69
+
70
+ us_expanded.product(rest_expanded).collect { |x, y| x + y }
71
+ end
72
+
73
+ ##
74
+ # Удалить дубликаты из списка
75
+ def remove_duplicates(l)
76
+ l.to_set.to_a
77
+ end
78
+
79
+ ##
80
+ # Expand a hostlist expression string to list
81
+ #
82
+ # Example: expand_hostlist("n[9-11],d[01-02]") ==>
83
+ # ['n9', 'n10', 'n11', 'd01', 'd02']
84
+ def expand_hostlist(hostlist, allow_duplicates=false)
85
+ results = []
86
+ bracket_level = 0
87
+ part = ''
88
+
89
+ (hostlist + ',').each_char do |c|
90
+ if c == ',' and bracket_level == 0
91
+ if part
92
+ results.concat(expand_part part)
93
+ end
94
+
95
+ part = ''
96
+ bad_part = false
97
+ else
98
+ part += c
99
+ end
100
+
101
+ bracket_level += 1 if c == '['
102
+ bracket_level -= 1 if c == ']'
103
+
104
+ if bracket_level > 1
105
+ raise 'Error! Bad hostlist (nested brackets)'
106
+ elsif bracket_level < 0
107
+ raise 'Error! Bad hostlist (unbalanced brackets)'
108
+ end
109
+ end
110
+
111
+ if bracket_level > 0
112
+ raise 'Error! Bad hostlist (unbalanced brackets)'
113
+ end
114
+
115
+ return remove_duplicates(results) unless allow_duplicates
116
+
117
+ results
118
+ end
119
+
120
+ ##
121
+ # Collect a hostlist string from a list of hosts
122
+ def collect_hostlist(hosts, silently_discard_bad = false)
123
+ left_right = []
124
+
125
+ hosts.each do |host|
126
+ # Remove leading and trailing whitespace first, and skip empty lines
127
+ host = host.strip
128
+ next if host.empty?
129
+
130
+ # We cannot accept a host containing any of the three special
131
+ # characters in the hostlist syntax (comma and flat brackets)
132
+
133
+ unless host.scan(/[\[\]\,]/).empty?
134
+ next if silently_discard_bad
135
+
136
+ raise 'Error! Forbidden character!'
137
+ end
138
+
139
+ left_right.push [host, ""]
140
+ end
141
+
142
+ # Call the iterative function until it syas it's done
143
+ looping = true
144
+
145
+ while looping
146
+ left_right, looping = collect_hostlist_1 left_right
147
+ end
148
+
149
+ return (left_right.collect { |left, right| left + right }).join(",")
150
+ end
151
+
152
+ ##
153
+ # Format a range from low to high inclusively, with a certain width
154
+ def format_range(low, high, width)
155
+ return "%0#{width}d" % low if low == high
156
+
157
+ "%0#{width}d-" % low + "%0#{width}d" % high
158
+ end
159
+
160
+ ##
161
+ # Collect a hostlist string from a list of hosts (left + right)
162
+ def collect_hostlist_1(left_right, silently_discard_bad = false)
163
+ # Scan a list of hosts (left+right) and build two things:
164
+ # 1) a set of all hosts seen (used later)
165
+ # 2) a list where each host entry is preprocessed for correct sorting
166
+
167
+ sortlist = []
168
+ remeaning = Set.new
169
+
170
+ left_right.each do |left, right|
171
+ host = left + right
172
+ remeaning.add host
173
+
174
+ m = /^(.*?)([0-9]+)?([^0-9]*)$/.match left
175
+
176
+ prefix, num_str, suffix = m[1], m[2], m[3]
177
+
178
+ suffix = suffix + right
179
+
180
+ unless num_str
181
+ throw 'Error! Prefix is not empty' unless prefix.empty?
182
+ sortlist.push [[host, nil], nil, nil, host]
183
+ else
184
+ num_int = num_str.to_i
185
+ sortlist.push [[prefix, suffix], num_int, num_str.length, host]
186
+ end
187
+ end
188
+
189
+ sortlist.sort!
190
+
191
+ results = []
192
+ need_another_loop = false
193
+
194
+ sortlist.group_by { |x| x[0] }.each do |prefix_suffix, group|
195
+ prefix, suffix = prefix_suffix
196
+
197
+ unless suffix
198
+ # Special case: a host with no numeric part
199
+ results.push ["", prefix]
200
+ remeaning.delete prefix
201
+ else
202
+ # General case
203
+ range_list = []
204
+
205
+ group.each do |prefix_suffix_2, num_int, num_width, host|
206
+ prefix_2, suffix_2 = prefix_suffix_2
207
+
208
+ next unless remeaning.include? host
209
+ raise 'Error! num_int is nil' unless num_int
210
+
211
+ # Scan for a range starting at the current host
212
+ low = num_int
213
+
214
+ while true
215
+ host = "#{prefix}%0#{num_width}d#{suffix}" % num_int
216
+
217
+ if remeaning.include? host
218
+ remeaning.delete host
219
+ num_int += 1
220
+ else
221
+ break
222
+ end
223
+ end
224
+
225
+ high = num_int - 1
226
+
227
+ raise 'Error! High < Low' if high < low
228
+
229
+ range_list.push [low, high, num_width]
230
+ end
231
+
232
+ need_another_loop = true
233
+
234
+ if range_list.length == 1 and range_list[0][0] == range_list[0][1]
235
+ results.push [prefix, "%0#{range_list[0][2]}d#{suffix}" % range_list[0][0]]
236
+ else
237
+ results.push [prefix, "[" + range_list.map { |l, h, w| format_range(l, h, w)}.join(',') + "]" + suffix]
238
+ end
239
+ end
240
+ end
241
+
242
+ raise 'Error! Remeaning is not empty' unless remeaning.empty?
243
+
244
+ return results, need_another_loop
245
+ end
246
+
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hostlist-expand-collect
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Georgy Evtushenko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-03 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Hostlist on native ruby (adaptation of python-hostlist)
14
+ email: evtushenko.georgy@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/hostlist_expand_collect.rb
20
+ homepage: https://github.com/senior-zero/Hostlist
21
+ licenses:
22
+ - MIT
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubyforge_project:
40
+ rubygems_version: 2.5.1
41
+ signing_key:
42
+ specification_version: 4
43
+ summary: Hostlist on native ruby
44
+ test_files: []