1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import re
21 import errors
22
23 __all__ = [
24 'NameParser',
25 ]
26
27 from deprecation import deprecated_callable
28
32
36
40
42
43 """Parse Arch names with with tla.
44
45 All operations run tla, this is generally too expensive for
46 practical use. Use NameParser instead, which is implemented
47 natively.
48
49 This class is retained for testing purposes.
50 """
51
53 opts = [ 'valid-package-name' ]
54 if self.startswith('-'):
55 return False
56 if tolerant:
57 opts.append('--tolerant')
58 if opt:
59 opts.append(opt)
60 opts.append(self)
61
62 return 0 == backend().status_cmd(opts, expected=(0,1,2))
63
65 opts = [ 'parse-package-name' ]
66 if self.startswith('-'):
67 return None
68 if opt:
69 opts.append(opt)
70 opts.append(self)
71
72 status, retval = backend().status_one_cmd(opts, expected=(0,1,2))
73 if status != 0:
74 return None
75 return retval
76
78 return self.__valid_package_name('--category', tolerant=False)
80 return self.__valid_package_name('--package', tolerant=False)
82 return self.__valid_package_name('--vsn', tolerant=False)
84 return self.__valid_package_name('--patch-level', tolerant=False)
85
87 return self.__valid_package_name('--archive')
89 return self.__valid_package_name('--category')
91 return self.__valid_package_name('--package')
93 return self.__valid_package_name('--vsn')
95 return self.__valid_package_name('--patch-level')
96
98 return self.__parse_package_name('--arch')
100 return self.__parse_package_name('--non-arch')
102 return self.__parse_package_name('--category')
104 return self.__parse_package_name('--branch')
106 return self.__parse_package_name('--package')
108 if not self.has_version(): return None
109 return self.__parse_package_name('--vsn')
111 return self.__parse_package_name('--package-version')
113 if not self.has_patchlevel(): return None
114 return self.__parse_package_name('--patch-level')
115
116
118
119 """Parser for names in Arch archive namespace.
120
121 Implements name parsing natively for performance reasons. It
122 should behave exactly as tla, any discrepancy is to be considered
123 a bug, unless tla is obviously buggy.
124
125 Bare names of archives, category, branch, versions ids, and
126 unqualified patchlevel names are not part of the archive
127 namespace. They can be validated using static methods.
128
129 :group Specificity level: is_category, is_package, is_version
130
131 :group Presence name components: has_archive, has_category, has_package,
132 has_version, has_revision, has_patchlevel
133
134 :group Getting name components: get_archive, get_nonarch, get_category,
135 get_branch, get_package, get_version, get_package_version,
136 get_patchlevel
137
138 :group Validating name components: is_archive_name, is_category_name,
139 is_branch_name, is_version_id, is_patchlevel
140 """
141
142 __archive_regex = re.compile('^[a-zA-Z0-9][-a-zA-Z0-9]*(\.[-a-zA-Z0-9]+)*@'
143 '[-a-zA-Z0-9.]*$')
144 __name_regex = re.compile('^[a-zA-Z]([a-zA-Z0-9]|-[a-zA-Z0-9])*$')
145 __version_regex = re.compile('^[0-9][A-Za-z0-9+:.~-]*$')
146
147 __level_regex = re.compile('^base-0$|^(version|patch|versionfix)-[0-9]+$')
148
150 """Create a parser object for the given string.
151
152 :param s: string to parse.
153 :type s: str
154 """
155 str.__init__(s)
156 parts = self.__parse()
157 if parts:
158 self.__valid = True
159 else:
160 parts = None, None, None, None, None, None
161 self.__valid = False
162 (self.__archive, self.__nonarch, self.__category,
163 self.__branch, self.__version, self.__level) = parts
164
166 parts = self.__split()
167 if not parts:
168 return False
169 if not self.__validate(parts):
170 return False
171 return parts
172
174 parts = self.split('/')
175 if len(parts) == 1:
176 archive, nonarch = None, parts[0]
177 elif len(parts) == 2:
178 archive, nonarch = parts
179 else:
180 return False
181 parts = nonarch.split('--')
182 category, branch, version, level = None, None, None, None
183 if len(parts) == 1:
184 category = parts[0]
185 elif len(parts) == 2:
186 if nonarch.endswith('--'):
187 category, ignored = parts
188 elif self.__name_regex.match(parts[1]):
189 category, branch = parts
190 elif self.__match_name_with_dash(parts[1]):
191 category, branch = parts
192 else:
193 category, version = parts
194 elif len(parts) == 3:
195 if nonarch.endswith('--'):
196 category, branch, ignored = parts
197 elif self.__name_regex.match(parts[1]):
198 category, branch, version = parts
199 else:
200 category, version, level = parts
201 elif len(parts) == 4:
202 category, branch, version, level = parts
203 else:
204 return False
205 return archive, nonarch, category, branch, version, level
206
208 if not string.endswith('-'):
209 return False
210 if self.__name_regex.match(string[:-1]):
211 return True
212 else:
213 return False
214
238
240 """Is this a category name?
241
242 :rtype: bool
243 """
244 return bool(self.__category) and not (self.__branch or self.__version)
245
247 """Is this a package name (category or branch name)?
248
249 :rtype: bool
250 """
251 return bool(self.__category) and not self.__version
252
254 """Is this a version name?
255
256 :rtype: bool
257 """
258 return bool(self.__version) and not self.__level
259
261 """Does this include an archive name?
262
263 :rtype: bool
264 """
265 return bool(self.__archive)
266
268 """Does this include an category name?
269
270 All valid names include a category.
271
272 :rtype: bool
273 """
274 return bool(self.__category)
275
277 """Does this include an package name?
278
279 All valid names include a package.
280
281 :rtype: bool
282 """
283 return bool(self.__category)
284
286 """Does this include a version name?
287
288 :rtype: bool
289 """
290 return bool(self.__version)
291
293 """Does this include a revision name?
294
295 :rtype: bool
296 """
297 return bool(self.__level)
298
300 """Get the archive part of the name
301
302 :return: archive part of the name, or the default archive name, or None
303 if the name is invalid.
304 :rtype: str, None
305 """
306 if not self.__valid: return None
307 if not self.__archive: return default_archive()
308 return self.__archive
309
311 """Get Non-archive part of the name
312
313 :return: the name without the archive component, or None if the name is
314 invalid or has no archive component.
315 :rtype: str, None
316 """
317 return self.__nonarch
318
320 """Get the Category name
321
322 :return: part of the name which identifies the category within
323 the archive, or None if the name is invalid or has no category
324 component.
325 :rtype: str, None
326 """
327 return self.__category
328
330 """Get the branch part of name
331
332 :return: part of the name which identifies the branch within the
333 category, or None if the name is invalid or the empty string if the
334 name has no branch component.
335 :rtype: str, None
336 """
337 if not self.__valid: return None
338 if not self.__branch: return str()
339 return self.__branch
340
342 """Get the package name
343
344 :return: part of the name including the category part and branch part
345 (if present) of the name, or None if the name is not valid.
346 :rtype: str, None
347 """
348 if not self.__valid: return None
349 if self.__branch is None: return self.__category
350 return self.__category + '--' + self.__branch
351
353 """Get the version id part of the name
354
355 :return: part of the name identifying a version in a branch, or None if
356 the name is invalid or does not contain a version id.
357 :rtype: str, None
358 """
359 return self.__version
360
362 """Get the unqualified version name
363
364 :return: part of the name identifying a version in an archive, or None
365 if the name does not contain a version id or is invalid.
366 :rtype: str, None
367 """
368 if not self.__version: return None
369 return self.get_package() + '--' + self.__version
370
372 """Get the patch-level part of the name
373
374 :return: part of the name identifying a patch in a version, or None if
375 the name is not a revision or is invalid.
376 :rtype: str, None
377 """
378 return self.__level
379
381 """Is this string a valid archive name?
382
383 :param s: string to validate.
384 :type s: str
385 :rtype: bool
386 """
387 return bool(klass.__archive_regex.match(s))
388 is_archive_name = classmethod(is_archive_name)
389
391 """Is this string a valid category name?
392
393 Currently does the same thing as is_branch_name, but that might
394 change in the future when the namespace evolves and it is more
395 expressive to have different functions.
396
397 :param s: string to validate.
398 :type s: str
399 :rtype: bool
400 """
401 return bool(klass.__name_regex.match(s))
402 is_category_name = classmethod(is_category_name)
403
405 """Is this string a valid category name?
406
407 Currently does the same thing as is_category_name, but that might
408 change in the future when the namespace evolves and it is more
409 expressive to have different functions.
410
411 :param s: string to validate.
412 :type s: str
413 :rtype: bool
414 """
415 return bool(klass.__name_regex.match(s))
416 is_branch_name = classmethod(is_branch_name)
417
419 """Is this string a valid version id?
420
421 :param s: string to validate.
422 :type s: str
423 :rtype: bool
424 """
425 return bool(klass.__version_regex.match(s))
426 is_version_id = classmethod(is_version_id)
427
429 """Is this string a valid unqualified patch-level name?
430
431 :param s: string to validate.
432 :type s: str
433 :rtype: bool
434 """
435 return bool(klass.__level_regex.match(s))
436 is_patchlevel = classmethod(is_patchlevel)
437
438
472