|
8 | 8 | import sys |
9 | 9 | import uuid |
10 | 10 | from functools import lru_cache |
| 11 | +from pathlib import Path |
11 | 12 | from threading import Thread |
12 | 13 | from typing import Any, Callable, Dict, Union |
13 | 14 |
|
@@ -74,8 +75,7 @@ def is_enabled(self): |
74 | 75 | return ( |
75 | 76 | os.environ.get(DO_NOT_TRACK_ENV, None) is None and self.enabled() |
76 | 77 | if callable(self.enabled) |
77 | | - else self.enabled |
78 | | - and _find_or_create_user_id() != DO_NOT_TRACK_VALUE |
| 78 | + else self.enabled and _find_or_create_user_id() is not None |
79 | 79 | ) |
80 | 80 |
|
81 | 81 | def send( |
@@ -154,7 +154,7 @@ def _runtime_info(self): |
154 | 154 | # "scm_class": _scm_in_use(), |
155 | 155 | **_system_info(), |
156 | 156 | "user_id": _find_or_create_user_id(), |
157 | | - "group_id": _find_or_create_user_id(), # TODO |
| 157 | + "group_id": "", # TODO |
158 | 158 | } |
159 | 159 |
|
160 | 160 |
|
@@ -187,39 +187,64 @@ def _system_info(): |
187 | 187 | raise NotImplementedError |
188 | 188 |
|
189 | 189 |
|
| 190 | +def _generate_id(): |
| 191 | + """A randomly generated ID string""" |
| 192 | + return str(uuid.uuid4()) # TODO: CI env-based ID |
| 193 | + |
| 194 | + |
| 195 | +def _read_user_id(config_file: Path): |
| 196 | + try: |
| 197 | + with config_file.open(encoding="utf8") as fobj: |
| 198 | + return json.load(fobj)["user_id"] |
| 199 | + except (FileNotFoundError, ValueError, KeyError): |
| 200 | + pass |
| 201 | + return None |
| 202 | + |
| 203 | + |
| 204 | +def _read_user_id_locked(config_file: Path): |
| 205 | + lockfile = str(config_file.with_suffix(".lock")) |
| 206 | + if config_file.parent.is_dir(): |
| 207 | + with FileLock(lockfile, timeout=5): |
| 208 | + return _read_user_id(config_file) |
| 209 | + return None |
| 210 | + |
| 211 | + |
190 | 212 | @lru_cache(None) |
191 | 213 | def _find_or_create_user_id(): |
192 | 214 | """ |
193 | 215 | The user's ID is stored on a file under the global config directory. |
194 | 216 | The file should contain a JSON with a "user_id" key: |
195 | 217 | {"user_id": "16fd2706-8baf-433b-82eb-8c7fada847da"} |
196 | | - IDs are generated randomly with UUID. |
| 218 | + IDs are generated randomly with UUID4. |
197 | 219 | """ |
198 | | - |
199 | | - config_dir = user_config_dir("telemetry", "iterative") |
200 | | - fname = os.path.join(config_dir, "user_id") |
201 | | - lockfile = os.path.join(config_dir, "user_id.lock") |
202 | | - |
203 | | - # Since the `fname` and `lockfile` are under the global config, |
204 | | - # we need to make sure such directory exist already. |
205 | | - os.makedirs(config_dir, exist_ok=True) |
| 220 | + config_file = Path( |
| 221 | + user_config_dir(os.path.join("iterative", "telemetry"), False) |
| 222 | + ) |
| 223 | + config_file.parent.mkdir(mode=0o755, parents=True, exist_ok=True) |
| 224 | + lockfile = str(config_file.with_suffix(".lock")) |
| 225 | + # DVC backwards-compatibility |
| 226 | + config_file_old = Path( |
| 227 | + user_config_dir(os.path.join("dvc", "user_id"), "iterative") |
| 228 | + ) |
206 | 229 |
|
207 | 230 | try: |
208 | 231 | with FileLock( # pylint: disable=abstract-class-instantiated |
209 | 232 | lockfile, timeout=5 |
210 | 233 | ): |
211 | | - try: |
212 | | - with open(fname, encoding="utf8") as fobj: |
213 | | - user_id = json.load(fobj)["user_id"] |
214 | | - |
215 | | - except (FileNotFoundError, ValueError, KeyError): |
216 | | - user_id = str(uuid.uuid4()) |
217 | | - |
218 | | - with open(fname, "w", encoding="utf8") as fobj: |
| 234 | + user_id = _read_user_id(config_file) |
| 235 | + if user_id is None: |
| 236 | + try: |
| 237 | + user_id = _read_user_id_locked(config_file_old) |
| 238 | + except Timeout: |
| 239 | + logger.debug( |
| 240 | + "Failed to acquire %s", |
| 241 | + config_file_old.with_suffix(".lock"), |
| 242 | + ) |
| 243 | + return None |
| 244 | + if user_id is None: |
| 245 | + user_id = _generate_id() |
| 246 | + with config_file.open(mode="w", encoding="utf8") as fobj: |
219 | 247 | json.dump({"user_id": user_id}, fobj) |
220 | | - |
221 | | - return user_id |
222 | | - |
223 | 248 | except Timeout: |
224 | 249 | logger.debug("Failed to acquire %s", lockfile) |
225 | | - return None |
| 250 | + return user_id if user_id.lower() != DO_NOT_TRACK_VALUE.lower() else None |
0 commit comments