dcicutils 6.9.9.0b4__py3-none-any.whl → 6.9.9.0b6__py3-none-any.whl
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.
- dcicutils/ff_mocks.py +17 -0
- dcicutils/ff_utils.py +16 -11
- dcicutils/misc_utils.py +12 -0
- dcicutils/redis_tools.py +20 -35
- dcicutils/redis_utils.py +22 -2
- dcicutils/secrets_utils.py +2 -1
- {dcicutils-6.9.9.0b4.dist-info → dcicutils-6.9.9.0b6.dist-info}/METADATA +1 -1
- {dcicutils-6.9.9.0b4.dist-info → dcicutils-6.9.9.0b6.dist-info}/RECORD +9 -9
- {dcicutils-6.9.9.0b4.dist-info → dcicutils-6.9.9.0b6.dist-info}/WHEEL +0 -0
    
        dcicutils/ff_mocks.py
    CHANGED
    
    | @@ -190,6 +190,23 @@ def mocked_s3utils(environments=None, require_sse=False, other_access_key_names= | |
| 190 190 | 
             
                                                    yield mock_boto3
         | 
| 191 191 |  | 
| 192 192 |  | 
| 193 | 
            +
            DEFAULT_SSE_ENVIRONMENTS_TO_MOCK = ['fourfront-foo', 'fourfront-bar']
         | 
| 194 | 
            +
             | 
| 195 | 
            +
             | 
| 196 | 
            +
            @contextlib.contextmanager
         | 
| 197 | 
            +
            def mocked_s3utils_with_sse(beanstalks=None, environments=None, require_sse=True, files=None):
         | 
| 198 | 
            +
                # beanstalks= is deprecated. Use environments= instead.
         | 
| 199 | 
            +
                environments = (DEFAULT_SSE_ENVIRONMENTS_TO_MOCK
         | 
| 200 | 
            +
                                if beanstalks is None and environments is None
         | 
| 201 | 
            +
                                else (beanstalks or []) + (environments or []))
         | 
| 202 | 
            +
                with mocked_s3utils(environments=environments, require_sse=require_sse) as mock_boto3:
         | 
| 203 | 
            +
                    s3 = mock_boto3.client('s3')
         | 
| 204 | 
            +
                    assert isinstance(s3, MockBotoS3Client)
         | 
| 205 | 
            +
                    for filename, string in (files or {}).items():
         | 
| 206 | 
            +
                        s3.s3_files.files[filename] = string.encode('utf-8')
         | 
| 207 | 
            +
                    yield mock_boto3
         | 
| 208 | 
            +
             | 
| 209 | 
            +
             | 
| 193 210 | 
             
            # Here we set up some variables, auxiliary functions, and mocks containing common values needed for testing
         | 
| 194 211 | 
             
            # of the next several functions so that the functions don't have to set them up over
         | 
| 195 212 | 
             
            # and over again in each test.
         | 
    
        dcicutils/ff_utils.py
    CHANGED
    
    | @@ -6,13 +6,11 @@ import requests | |
| 6 6 | 
             
            import time
         | 
| 7 7 |  | 
| 8 8 | 
             
            from collections import namedtuple
         | 
| 9 | 
            +
            from dcicutils.lang_utils import disjoined_list
         | 
| 9 10 | 
             
            from elasticsearch.exceptions import AuthorizationException
         | 
| 10 11 | 
             
            from typing import Dict, List
         | 
| 11 12 | 
             
            from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
         | 
| 12 | 
            -
            from . import  | 
| 13 | 
            -
                s3_utils,
         | 
| 14 | 
            -
                es_utils,
         | 
| 15 | 
            -
            )
         | 
| 13 | 
            +
            from . import s3_utils, es_utils
         | 
| 16 14 | 
             
            from .misc_utils import PRINT
         | 
| 17 15 |  | 
| 18 16 |  | 
| @@ -107,8 +105,6 @@ def search_request_with_retries(request_fxn, url, auth, verb, **kwargs): | |
| 107 105 | 
             
                    try:
         | 
| 108 106 | 
             
                        res_json = res.json()
         | 
| 109 107 | 
             
                    except ValueError:
         | 
| 110 | 
            -
                        # PyCharm notes this is unused. -kmp 17-Jul-2020
         | 
| 111 | 
            -
                        # res_json = {}
         | 
| 112 108 | 
             
                        try:
         | 
| 113 109 | 
             
                            res.raise_for_status()
         | 
| 114 110 | 
             
                        except Exception as e:
         | 
| @@ -238,7 +234,7 @@ def internal_compute_authorized_request(url, *, auth, ff_env, verb, retry_fxn, * | |
| 238 234 | 
             
                authorized_request('https://data.4dnucleome.org/<some path>', ff_env='fourfront-webprod')
         | 
| 239 235 | 
             
                """
         | 
| 240 236 | 
             
                # Save to uncomment if debugging unit tests...
         | 
| 241 | 
            -
                # print("authorized_request\n URL | 
| 237 | 
            +
                # print(f"authorized_request\n URL={url}\n auth={auth}\n ff_env={ff_env}\n verb={verb}\n")
         | 
| 242 238 | 
             
                use_auth = unified_authentication(auth, ff_env)
         | 
| 243 239 | 
             
                headers = kwargs.get('headers')
         | 
| 244 240 | 
             
                if not headers:
         | 
| @@ -246,11 +242,12 @@ def internal_compute_authorized_request(url, *, auth, ff_env, verb, retry_fxn, * | |
| 246 242 | 
             
                if 'timeout' not in kwargs:
         | 
| 247 243 | 
             
                    kwargs['timeout'] = 60  # default timeout
         | 
| 248 244 |  | 
| 245 | 
            +
                verb_upper = verb
         | 
| 249 246 | 
             
                try:
         | 
| 250 | 
            -
                     | 
| 247 | 
            +
                    verb_upper = verb.upper()
         | 
| 248 | 
            +
                    the_verb = REQUESTS_VERBS[verb_upper]
         | 
| 251 249 | 
             
                except KeyError:
         | 
| 252 | 
            -
                    raise ValueError("Provided verb  | 
| 253 | 
            -
                                     % (verb.upper(), ', '.join(REQUESTS_VERBS.keys())))
         | 
| 250 | 
            +
                    raise ValueError(f"Provided verb {verb} is not valid. Must be one of {disjoined_list(REQUESTS_VERBS)}.")
         | 
| 254 251 | 
             
                # automatically detect a search and overwrite the retry if it is standard
         | 
| 255 252 | 
             
                if '/search/' in url and retry_fxn == standard_request_with_retries:
         | 
| 256 253 | 
             
                    retry_fxn = search_request_with_retries
         | 
| @@ -262,7 +259,15 @@ def internal_compute_authorized_request(url, *, auth, ff_env, verb, retry_fxn, * | |
| 262 259 |  | 
| 263 260 |  | 
| 264 261 | 
             
            def _sls(val):
         | 
| 265 | 
            -
                """ | 
| 262 | 
            +
                """
         | 
| 263 | 
            +
                Helper to strip any leading slashes on ids in API functions.
         | 
| 264 | 
            +
                Examples:
         | 
| 265 | 
            +
                    >>> _sls('foo')
         | 
| 266 | 
            +
                    foo
         | 
| 267 | 
            +
                    >>> _sls('/foo')
         | 
| 268 | 
            +
                    foo
         | 
| 269 | 
            +
                    >>> _sls('/foo/bar/baz/')
         | 
| 270 | 
            +
                    foo/bar/baz/
         | 
| 266 271 | 
             
                """
         | 
| 267 272 | 
             
                return val.lstrip('/')
         | 
| 268 273 |  | 
    
        dcicutils/misc_utils.py
    CHANGED
    
    | @@ -12,6 +12,7 @@ import io | |
| 12 12 | 
             
            import os
         | 
| 13 13 | 
             
            import logging
         | 
| 14 14 | 
             
            import pytz
         | 
| 15 | 
            +
            import re
         | 
| 15 16 | 
             
            import rfc3986.validators
         | 
| 16 17 | 
             
            import rfc3986.exceptions
         | 
| 17 18 | 
             
            import time
         | 
| @@ -1316,6 +1317,17 @@ def string_list(s): | |
| 1316 1317 | 
             
                return [p for p in [part.strip() for part in s.split(",")] if p]
         | 
| 1317 1318 |  | 
| 1318 1319 |  | 
| 1320 | 
            +
            def is_c4_arn(arn: str) -> bool:
         | 
| 1321 | 
            +
                """
         | 
| 1322 | 
            +
                Returns True iff the given (presumed) AWS ARN string value looks like it
         | 
| 1323 | 
            +
                refers to a CGAP or Fourfront Cloudformation entity, otherwise returns False.
         | 
| 1324 | 
            +
                :param arn: String representing an AWS ARN.
         | 
| 1325 | 
            +
                :return: True if the given ARN refers to a CGAP or Fourfront Cloudformation entity, else False.
         | 
| 1326 | 
            +
                """
         | 
| 1327 | 
            +
                pattern = r"(fourfront|cgap|[:/]c4-)"
         | 
| 1328 | 
            +
                return True if re.search(pattern, arn) else False
         | 
| 1329 | 
            +
             | 
| 1330 | 
            +
             | 
| 1319 1331 | 
             
            def string_md5(unicode_string):
         | 
| 1320 1332 | 
             
                """
         | 
| 1321 1333 | 
             
                Returns the md5 signature for the given u unicode string.
         | 
    
        dcicutils/redis_tools.py
    CHANGED
    
    | @@ -24,29 +24,16 @@ class RedisSessionToken: | |
| 24 24 | 
             
                Model used by Redis to store session tokens
         | 
| 25 25 | 
             
                Keystore structure:
         | 
| 26 26 | 
             
                    <env_namespace>:session:<token>
         | 
| 27 | 
            -
                        -> Redis  | 
| 27 | 
            +
                        -> Redis str of JWT with automated expiration
         | 
| 28 28 | 
             
                """
         | 
| 29 | 
            -
                JWT = 'jwt'
         | 
| 30 | 
            -
                EXPIRATION = 'expiration'
         | 
| 31 29 |  | 
| 32 30 | 
             
                @staticmethod
         | 
| 33 | 
            -
                def _build_session_expiration(n_hours: int = 3) ->  | 
| 31 | 
            +
                def _build_session_expiration(n_hours: int = 3) -> datetime.timedelta:
         | 
| 34 32 | 
             
                    """ Builds a session expiration date 3 hours after generation
         | 
| 35 33 | 
             
                    :param n_hours: timestamp after which the session token is invalid
         | 
| 36 34 | 
             
                    :return: datetime string 3 hours from creation time
         | 
| 37 35 | 
             
                    """
         | 
| 38 | 
            -
                    return  | 
| 39 | 
            -
             | 
| 40 | 
            -
                def _build_session_hset(self, jwt: str, expiration=None) -> dict:
         | 
| 41 | 
            -
                    """ Builds Redis hset record for the session token
         | 
| 42 | 
            -
                    :param jwt: encoded jwt value
         | 
| 43 | 
            -
                    :param expiration: expiration if using an existing one
         | 
| 44 | 
            -
                    :return: dictionary to be stored as a hash in Redis
         | 
| 45 | 
            -
                    """
         | 
| 46 | 
            -
                    return {
         | 
| 47 | 
            -
                        self.JWT: jwt,
         | 
| 48 | 
            -
                        self.EXPIRATION: self._build_session_expiration() if not expiration else expiration
         | 
| 49 | 
            -
                    }
         | 
| 36 | 
            +
                    return datetime.timedelta(hours=n_hours)
         | 
| 50 37 |  | 
| 51 38 | 
             
                @staticmethod
         | 
| 52 39 | 
             
                def _build_redis_key(namespace: str, token: str) -> str:
         | 
| @@ -72,23 +59,23 @@ class RedisSessionToken: | |
| 72 59 | 
             
                    self.namespace = namespace
         | 
| 73 60 | 
             
                    self.redis_key = self._build_redis_key(self.namespace, self.session_token)
         | 
| 74 61 | 
             
                    self.jwt = jwt
         | 
| 75 | 
            -
                    self. | 
| 62 | 
            +
                    self.expiration = expiration or self._build_session_expiration()
         | 
| 76 63 |  | 
| 77 64 | 
             
                def __eq__(self, other):
         | 
| 78 65 | 
             
                    """ Evaluates equality of two session objects based on the value of the session hset """
         | 
| 79 | 
            -
                    return self. | 
| 66 | 
            +
                    return (self.redis_key == other.redis_key) and (self.jwt == other.jwt)
         | 
| 80 67 |  | 
| 81 68 | 
             
                def get_session_token(self) -> str:
         | 
| 82 69 | 
             
                    """ Extracts the session token stored on this object """
         | 
| 83 70 | 
             
                    return self.session_token
         | 
| 84 71 |  | 
| 85 72 | 
             
                def get_redis_key(self) -> str:
         | 
| 86 | 
            -
                    """ Returns the key under which the Redis  | 
| 73 | 
            +
                    """ Returns the key under which the Redis set is stored - note that this contains the token! """
         | 
| 87 74 | 
             
                    return self.redis_key
         | 
| 88 75 |  | 
| 89 | 
            -
                def get_expiration(self) ->  | 
| 90 | 
            -
                    """ Returns the expiration  | 
| 91 | 
            -
                    return self. | 
| 76 | 
            +
                def get_expiration(self) -> datetime:
         | 
| 77 | 
            +
                    """ Returns the expiration timedelta """
         | 
| 78 | 
            +
                    return self.expiration
         | 
| 92 79 |  | 
| 93 80 | 
             
                @classmethod
         | 
| 94 81 | 
             
                def from_redis(cls, *, redis_handler: RedisBase, namespace: str, token: str):
         | 
| @@ -100,10 +87,11 @@ class RedisSessionToken: | |
| 100 87 | 
             
                    :return: A RedisSessionToken object built from an existing record in Redis
         | 
| 101 88 | 
             
                    """
         | 
| 102 89 | 
             
                    redis_key = f'{namespace}:session:{token}'
         | 
| 103 | 
            -
                    redis_entry = redis_handler. | 
| 90 | 
            +
                    redis_entry = redis_handler.get(redis_key)
         | 
| 104 91 | 
             
                    if redis_entry:
         | 
| 105 | 
            -
                         | 
| 106 | 
            -
             | 
| 92 | 
            +
                        expiration = redis_handler.ttl(redis_key)
         | 
| 93 | 
            +
                        return cls(namespace=namespace, jwt=redis_entry,
         | 
| 94 | 
            +
                                   token=token, expiration=expiration)
         | 
| 107 95 |  | 
| 108 96 | 
             
                def decode_jwt(self, audience: str, secret: str, leeway: int = 30) -> dict:
         | 
| 109 97 | 
             
                    """ Decodes JWT to grab info such as the email
         | 
| @@ -121,7 +109,7 @@ class RedisSessionToken: | |
| 121 109 | 
             
                    :return: True if successful, raise Exception otherwise
         | 
| 122 110 | 
             
                    """
         | 
| 123 111 | 
             
                    try:
         | 
| 124 | 
            -
                        redis_handler. | 
| 112 | 
            +
                        redis_handler.set(self.redis_key, self.jwt, exp=self.expiration)
         | 
| 125 113 | 
             
                    except Exception as e:
         | 
| 126 114 | 
             
                        log.error(str(e))
         | 
| 127 115 | 
             
                        raise RedisException()
         | 
| @@ -132,15 +120,12 @@ class RedisSessionToken: | |
| 132 120 | 
             
                    :param redis_handler: handle to Redis API
         | 
| 133 121 | 
             
                    :return: True if token matches that in Redis and is not expired
         | 
| 134 122 | 
             
                    """
         | 
| 135 | 
            -
                    redis_token = redis_handler. | 
| 123 | 
            +
                    redis_token = redis_handler.get(self.redis_key)
         | 
| 136 124 | 
             
                    if not redis_token:
         | 
| 137 125 | 
             
                        return False  # if it doesn't exist it's not valid
         | 
| 138 | 
            -
                     | 
| 139 | 
            -
                        redis_token[self.EXPIRATION]) > datetime.datetime.utcnow()
         | 
| 140 | 
            -
                    )
         | 
| 141 | 
            -
                    return timestamp_is_valid
         | 
| 126 | 
            +
                    return True  # if it does exist it must be valid since we always send with TTL
         | 
| 142 127 |  | 
| 143 | 
            -
                def update_session_token(self, *, redis_handler: RedisBase, jwt) -> bool:
         | 
| 128 | 
            +
                def update_session_token(self, *, redis_handler: RedisBase, jwt: str) -> bool:
         | 
| 144 129 | 
             
                    """ Refreshes the session token, jwt (if different) and expiration stored in Redis
         | 
| 145 130 | 
             
                    :param redis_handler: handle to Redis API
         | 
| 146 131 | 
             
                    :param jwt: jwt of user
         | 
| @@ -151,13 +136,13 @@ class RedisSessionToken: | |
| 151 136 | 
             
                    # build new one
         | 
| 152 137 | 
             
                    self.session_token = make_session_token()
         | 
| 153 138 | 
             
                    self.redis_key = self._build_redis_key(self.namespace, self.session_token)
         | 
| 139 | 
            +
                    self.expiration = self._build_session_expiration()
         | 
| 154 140 | 
             
                    self.jwt = jwt
         | 
| 155 | 
            -
                    self.session_hset = self._build_session_hset(jwt)
         | 
| 156 141 | 
             
                    return self.store_session_token(redis_handler=redis_handler)
         | 
| 157 142 |  | 
| 158 | 
            -
                def delete_session_token(self, *, redis_handler) -> bool:
         | 
| 143 | 
            +
                def delete_session_token(self, *, redis_handler: RedisBase) -> bool:
         | 
| 159 144 | 
             
                    """ Deletes the session token from redis, effectively logging out
         | 
| 160 145 | 
             
                    :param redis_handler: handle to Redis API
         | 
| 161 146 | 
             
                    :return: True if successful, False otherwise
         | 
| 162 147 | 
             
                    """
         | 
| 163 | 
            -
                    return redis_handler.delete(self.redis_key)
         | 
| 148 | 
            +
                    return 1 == redis_handler.delete(self.redis_key)
         | 
    
        dcicutils/redis_utils.py
    CHANGED
    
    | @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            import redis
         | 
| 2 | 
            +
            import datetime
         | 
| 2 3 | 
             
            from typing import Union
         | 
| 3 4 | 
             
            # Low level utilities for working with Redis
         | 
| 4 5 |  | 
| @@ -59,13 +60,17 @@ class RedisBase(object): | |
| 59 60 | 
             
                    """
         | 
| 60 61 | 
             
                    return self.redis.info()
         | 
| 61 62 |  | 
| 62 | 
            -
                def set(self, key: str, value: Union[str, int, float]) -> str:
         | 
| 63 | 
            +
                def set(self, key: str, value: Union[str, int, float], exp: Union[int, datetime.timedelta] = None) -> str:
         | 
| 63 64 | 
             
                    """ Sets the given key to the given value https://redis.io/commands/set/
         | 
| 64 65 | 
             
                    :param key: string to store value under
         | 
| 65 66 | 
             
                    :param value: value mapped to from key
         | 
| 67 | 
            +
                    :param exp: expiration time in seconds as int or datetime.timedelta (optional)
         | 
| 66 68 | 
             
                    :return: a "true" string <OK>
         | 
| 67 69 | 
             
                    """
         | 
| 68 | 
            -
                     | 
| 70 | 
            +
                    kwargs = {}
         | 
| 71 | 
            +
                    if exp:
         | 
| 72 | 
            +
                        kwargs['ex'] = exp
         | 
| 73 | 
            +
                    return self.redis.set(self._encode_value(key), self._encode_value(value), **kwargs)
         | 
| 69 74 |  | 
| 70 75 | 
             
                def get(self, key: str) -> str:
         | 
| 71 76 | 
             
                    """ Gets the given key from Redis https://redis.io/commands/get/
         | 
| @@ -77,6 +82,21 @@ class RedisBase(object): | |
| 77 82 | 
             
                        val = self._decode_value(val)
         | 
| 78 83 | 
             
                    return val
         | 
| 79 84 |  | 
| 85 | 
            +
                def set_expiration(self, key: str, t: Union[int, datetime.time]) -> bool:
         | 
| 86 | 
            +
                    """ Sets the TTL of the given key manually
         | 
| 87 | 
            +
                    :param key: key to set TTL
         | 
| 88 | 
            +
                    :param t: time in seconds until expiration (datetime.timedelta or int)
         | 
| 89 | 
            +
                    :returns: True if successful
         | 
| 90 | 
            +
                    """
         | 
| 91 | 
            +
                    return self.redis.expire(key, t, gt=True)
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                def ttl(self, key: str) -> datetime.time:
         | 
| 94 | 
            +
                    """ Gets the TTL of the given key
         | 
| 95 | 
            +
                    :param key: key to get TTL for
         | 
| 96 | 
            +
                    :return: datetime value in seconds
         | 
| 97 | 
            +
                    """
         | 
| 98 | 
            +
                    return self.redis.ttl(key)
         | 
| 99 | 
            +
             | 
| 80 100 | 
             
                def delete(self, key: str) -> int:
         | 
| 81 101 | 
             
                    """ Deletes the given key from Redis https://redis.io/commands/del/
         | 
| 82 102 | 
             
                    :param key: key to delete
         | 
    
        dcicutils/secrets_utils.py
    CHANGED
    
    | @@ -117,7 +117,8 @@ def apply_overrides(*, secrets: dict, rename_keys: Optional[dict] = None, | |
| 117 117 | 
             
                            raise ValueError(f"Cannot rename {key} to {new_name}"
         | 
| 118 118 | 
             
                                             f" because {a_or_an(new_name)} attribute already exists.")
         | 
| 119 119 | 
             
                        if key not in secrets:
         | 
| 120 | 
            -
                             | 
| 120 | 
            +
                            logger.warning(f"Cannot rename {key} to {new_name} because the secrets has no {key} attribute.")
         | 
| 121 | 
            +
                            continue
         | 
| 121 122 | 
             
                        secrets[new_name] = secrets.pop(key)
         | 
| 122 123 | 
             
                if override_values:
         | 
| 123 124 | 
             
                    secrets = dict(secrets, **override_values)
         | 
| @@ -20,23 +20,23 @@ dcicutils/env_utils.py,sha256=MQ3zcXcbxJV9dO5y1nN2HNswtiUw2VDK4KrROrZOUtA,46285 | |
| 20 20 | 
             
            dcicutils/env_utils_legacy.py,sha256=J81OAtJHN69o1beHO6q1j7_J6TeblSjnAHlS8VA5KSM,29032
         | 
| 21 21 | 
             
            dcicutils/es_utils.py,sha256=ZksLh5ei7kRUfiFltk8sd2ZSfh15twbstrMzBr8HNw4,7541
         | 
| 22 22 | 
             
            dcicutils/exceptions.py,sha256=fO-zIwcT0kdy-BL1qczva3QLY9ZEkhAFVGfBS9w-Row,9257
         | 
| 23 | 
            -
            dcicutils/ff_mocks.py,sha256= | 
| 24 | 
            -
            dcicutils/ff_utils.py,sha256= | 
| 23 | 
            +
            dcicutils/ff_mocks.py,sha256=wmf7-_C0cq9bpZZGUyKQO6eC-1p2ltKPVCSi-ln5y68,37025
         | 
| 24 | 
            +
            dcicutils/ff_utils.py,sha256=nHrVyf252HgP1mnerFmqJzUo6_Cm2hRk-Rnj5KN1RUQ,64324
         | 
| 25 25 | 
             
            dcicutils/jh_utils.py,sha256=Gpsxb9XEzggF_-Eq3ukjKvTnuyb9V1SCSUXkXsES4Kg,11502
         | 
| 26 26 | 
             
            dcicutils/kibana/dashboards.json,sha256=wHMB_mpJ8OaYhRRgvpZuihaB2lmSF64ADt_8hkBWgQg,16225
         | 
| 27 27 | 
             
            dcicutils/kibana/readme.md,sha256=3KmHF9FH6A6xwYsNxRFLw27q0XzHYnjZOlYUnn3VkQQ,2164
         | 
| 28 28 | 
             
            dcicutils/lang_utils.py,sha256=MaCJG-4KB2oOd3wcdd0e048eRBLkoxCKFkqR1KlrjkI,27302
         | 
| 29 29 | 
             
            dcicutils/log_utils.py,sha256=7pWMc6vyrorUZQf-V-M3YC6zrPgNhuV_fzm9xqTPph0,10883
         | 
| 30 | 
            -
            dcicutils/misc_utils.py,sha256= | 
| 30 | 
            +
            dcicutils/misc_utils.py,sha256=jRazVlj2gJ_DZsI2JjPIEfTmFLkcxDwPQStqGtaGMRc,86651
         | 
| 31 31 | 
             
            dcicutils/obfuscation_utils.py,sha256=xUCQYAe-nQY3vCKMLjT_3tAFhIeYkJuhMaZnxunF0Hc,5510
         | 
| 32 32 | 
             
            dcicutils/opensearch_utils.py,sha256=V2exmFYW8Xl2_pGFixF4I2Cc549Opwe4PhFi5twC0M8,1017
         | 
| 33 33 | 
             
            dcicutils/qa_checkers.py,sha256=hdduo-6ZITEurrmZYjll5N-nKQeyKaFLbERE9d4gRdU,20232
         | 
| 34 34 | 
             
            dcicutils/qa_utils.py,sha256=g0nuAigZUWCXd4ZBqOKHxLq1H3AGI2X2wD57rvBdAOY,120561
         | 
| 35 | 
            -
            dcicutils/redis_tools.py,sha256= | 
| 36 | 
            -
            dcicutils/redis_utils.py,sha256= | 
| 35 | 
            +
            dcicutils/redis_tools.py,sha256=VQ5cTnDnKQ8AX0n9iuikCqtEUtIlE0ZOEqVZR3iNGTI,6389
         | 
| 36 | 
            +
            dcicutils/redis_utils.py,sha256=VJ-7g8pOZqR1ZCtdcjKz3-6as2DMUcs1b1zG6wSprH4,6462
         | 
| 37 37 | 
             
            dcicutils/s3_utils.py,sha256=PAIUXpv7l7NNK0IzVLS6URJhLxg-SxLbZc0fJTpjI8E,25659
         | 
| 38 | 
            -
            dcicutils/secrets_utils.py,sha256= | 
| 38 | 
            +
            dcicutils/secrets_utils.py,sha256=8dppXAsiHhJzI6NmOcvJV5ldvKkQZzh3Fl-cb8Wm7MI,19745
         | 
| 39 39 | 
             
            dcicutils/snapshot_utils.py,sha256=ymP7PXH6-yEiXAt75w0ldQFciGNqWBClNxC5gfX2FnY,22961
         | 
| 40 | 
            -
            dcicutils-6.9.9. | 
| 41 | 
            -
            dcicutils-6.9.9. | 
| 42 | 
            -
            dcicutils-6.9.9. | 
| 40 | 
            +
            dcicutils-6.9.9.0b6.dist-info/WHEEL,sha256=DA86_h4QwwzGeRoz62o1svYt5kGEXpoUTuTtwzoTb30,83
         | 
| 41 | 
            +
            dcicutils-6.9.9.0b6.dist-info/METADATA,sha256=oWx6nepGIzhI4H0gi13-CV8KXVaYXtaSRmi8R4F1GUM,2722
         | 
| 42 | 
            +
            dcicutils-6.9.9.0b6.dist-info/RECORD,,
         | 
| 
            File without changes
         |