Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2016 The LUCI Authors. All rights reserved. | 1 # Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import re | 5 import re |
| 6 import types | 6 import types |
| 7 | 7 |
| 8 _SEGMENT_RE_BASE = r'[a-zA-Z0-9][a-zA-Z0-9:_\-.]*' | 8 _SEGMENT_RE_BASE = r'[a-zA-Z0-9][a-zA-Z0-9:_\-.]*' |
| 9 _STREAM_NAME_RE = re.compile('^(' + _SEGMENT_RE_BASE + ')(/' + | 9 _STREAM_NAME_RE = re.compile('^(' + _SEGMENT_RE_BASE + ')(/' + |
| 10 _SEGMENT_RE_BASE + ')*$') | 10 _SEGMENT_RE_BASE + ')*$') |
| 11 _MAX_STREAM_NAME_LENGTH = 4096 | 11 _MAX_STREAM_NAME_LENGTH = 4096 |
| 12 | 12 |
| 13 _MAX_TAG_KEY_LENGTH = 64 | 13 _MAX_TAG_KEY_LENGTH = 64 |
| 14 _MAX_TAG_VALUE_LENGTH = 4096 | 14 _MAX_TAG_VALUE_LENGTH = 4096 |
| 15 | 15 |
| 16 | |
| 16 def validate_stream_name(v, maxlen=None): | 17 def validate_stream_name(v, maxlen=None): |
| 17 """Verifies that a given stream name is valid. | 18 """Verifies that a given stream name is valid. |
| 18 | 19 |
| 19 Args: | 20 Args: |
| 20 v (str): The stream name string. | 21 v (str): The stream name string. |
| 21 | 22 |
| 22 | 23 |
| 23 Raises: | 24 Raises: |
| 24 ValueError if the stream name is invalid. | 25 ValueError if the stream name is invalid. |
| 25 """ | 26 """ |
| 26 maxlen = maxlen or _MAX_STREAM_NAME_LENGTH | 27 maxlen = maxlen or _MAX_STREAM_NAME_LENGTH |
| 27 if len(v) > maxlen: | 28 if len(v) > maxlen: |
| 28 raise ValueError('Maximum length exceeded (%d > %d)' % (len(v), maxlen)) | 29 raise ValueError('Maximum length exceeded (%d > %d)' % (len(v), maxlen)) |
| 29 if _STREAM_NAME_RE.match(v) is None: | 30 if _STREAM_NAME_RE.match(v) is None: |
| 30 raise ValueError('Invalid stream name') | 31 raise ValueError('Invalid stream name') |
| 31 | 32 |
| 32 | 33 |
| 33 def validate_tag(key, value): | 34 def validate_tag(key, value): |
| 34 """Verifies that a given tag key/value is valid. | 35 """Verifies that a given tag key/value is valid. |
| 35 | 36 |
| 36 Args: | 37 Args: |
| 37 k (str): The tag key. | 38 k (str): The tag key. |
| 38 v (str): The tag value. | 39 v (str): The tag value. |
| 39 | 40 |
| 40 Raises: | 41 Raises: |
| 41 ValueError if the tag is not valid. | 42 ValueError if the tag is not valid. |
| 42 """ | 43 """ |
| 43 validate_stream_name(key, maxlen=_MAX_TAG_KEY_LENGTH) | 44 validate_stream_name(key, maxlen=_MAX_TAG_KEY_LENGTH) |
| 44 validate_stream_name(value, maxlen=_MAX_TAG_VALUE_LENGTH) | 45 validate_stream_name(value, maxlen=_MAX_TAG_VALUE_LENGTH) |
| 46 | |
| 47 | |
| 48 def normalize(v, prefix=None): | |
| 49 """Given a string, "v", mutate it into a valid stream name. | |
| 50 | |
| 51 This operates by replacing invalid stream naem characters with underscores (_) | |
| 52 when encountered. | |
| 53 | |
| 54 A special case is when "v" begins with an invalid character. In this case, we | |
| 55 will replace it with the "prefix", if one is supplied. | |
| 56 | |
| 57 See _STREAM_NAME_RE for a description of a valid stream name. | |
| 58 | |
| 59 Raises: | |
| 60 ValueError: If normalization could not be successfully performed. | |
| 61 """ | |
| 62 if len(v) == 0: | |
| 63 if not prefix: | |
| 64 raise ValueError('Cannot normalize empty name with no prefix.') | |
| 65 v = prefix | |
| 66 else: | |
| 67 out = [] | |
| 68 for i, ch in enumerate(v): | |
| 69 if i == 0 and not _is_valid_stream_char(ch, first=True): | |
| 70 # The first letter is special, and must be alphanumeric. | |
| 71 # If we have a prefix, prepend that to the resulting string. | |
| 72 if prefix is None: | |
| 73 raise ValueError('Name has invalid beginning, and no prefix was ' | |
| 74 'provided.') | |
| 75 out.append(prefix) | |
|
martiniss
2016/08/11 20:24:43
Doing iteration over every character in a string l
dnj
2016/08/11 20:42:29
If you know a better way to do this, LMK.
| |
| 76 | |
| 77 if not _is_valid_stream_char(ch): | |
| 78 ch = '_' | |
| 79 out.append(ch) | |
| 80 v = ''.join(out) | |
| 81 | |
| 82 # Validate the resulting string. | |
| 83 validate_stream_name(v) | |
| 84 return v | |
| 85 | |
| 86 | |
| 87 def _is_valid_stream_char(ch, first=False): | |
| 88 """Returns (bool): True if a character is alphanumeric. | |
| 89 | |
| 90 The first character must be alphanumeric, matching [a-zA-Z0-9]. | |
| 91 Additional characters must either be alphanumeric or one of: (: _ - .). | |
| 92 | |
| 93 Args: | |
| 94 ch (str): the character to evaluate. | |
| 95 first (bool): if true, apply special first-character constraints. | |
| 96 """ | |
| 97 # Alphanumeric check. | |
| 98 ch_ord = ord(ch) | |
| 99 if ((ch_ord >= ord('0') and ch_ord <= ord('9')) or | |
|
martiniss
2016/08/11 20:24:43
Why not use https://docs.python.org/2/library/stri
dnj
2016/08/11 20:42:29
This seems faster, but might as well.
| |
| 100 (ch_ord >= ord('a') and ch_ord <= ord('z')) or | |
| 101 (ch_ord >= ord('A') and ch_ord <= ord('Z'))): | |
| 102 return True | |
| 103 if first: | |
| 104 # The first character must be alphanumeric. | |
| 105 return False | |
| 106 | |
| 107 # Check additional middle-name characters: | |
| 108 return ch in ':_-./' | |
| OLD | NEW |