forked from usnistgov/CASE-Utilities-Python
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathlocal_uuid.py
145 lines (116 loc) · 5.76 KB
/
local_uuid.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env python3
# This software was developed at the National Institute of Standards
# and Technology by employees of the Federal Government in the course
# of their official duties. Pursuant to title 17 Section 105 of the
# United States Code this software is not subject to copyright
# protection and is in the public domain. NIST assumes no
# responsibility whatsoever for its use by other parties, and makes
# no guarantees, expressed or implied, about its quality,
# reliability, or any other characteristic.
#
# We would appreciate acknowledgement if the software is used.
"""
This library is a wrapper for uuid, provided to generate repeatable UUIDs if requested.
"""
__version__ = "0.3.1"
import logging
import os
import pathlib
import sys
import typing
import uuid
import warnings
DEMO_UUID_BASE: typing.Optional[str] = None
DEMO_UUID_COUNTER: int = 0
_logger = logging.getLogger(pathlib.Path(__file__).name)
def configure() -> None:
global DEMO_UUID_BASE
if os.getenv("DEMO_UUID_REQUESTING_NONRANDOM") == "NONRANDOM_REQUESTED":
warnings.warn(
"Environment variable DEMO_UUID_REQUESTING_NONRANDOM is deprecated. See case_utils.local_uuid.demo_uuid for usage notes on its replacement, CASE_DEMO_NONRANDOM_UUID_BASE. Proceeding with random UUIDs.",
FutureWarning,
)
return
env_base_dir_name = os.getenv("CASE_DEMO_NONRANDOM_UUID_BASE")
if env_base_dir_name is None:
return
base_dir_original_path = pathlib.Path(env_base_dir_name)
if not base_dir_original_path.exists():
warnings.warn(
"Environment variable CASE_DEMO_NONRANDOM_UUID_BASE is expected to refer to an existing directory. Proceeding with random UUIDs.",
RuntimeWarning,
)
return
if not base_dir_original_path.is_dir():
warnings.warn(
"Environment variable CASE_DEMO_NONRANDOM_UUID_BASE is expected to refer to a directory. Proceeding with random UUIDs.",
RuntimeWarning,
)
return
# Component: An emphasis this is an example.
demo_uuid_base_parts = ["example.org"]
# Component: Present working directory, relative to CASE_DEMO_NONRANDOM_UUID_BASE if that environment variable is an ancestor of pwd.
base_dir_resolved_path = base_dir_original_path.resolve()
srcdir_original_path = pathlib.Path(os.getcwd())
srcdir_resolved_path = srcdir_original_path.resolve()
# _logger.debug("base_dir_resolved_path = %r.", base_dir_resolved_path)
# _logger.debug("srcdir_resolved_path = %r.", srcdir_resolved_path)
try:
srcdir_relative_path = srcdir_resolved_path.relative_to(base_dir_resolved_path)
# _logger.debug("srcdir_relative_path = %r.", srcdir_relative_path)
demo_uuid_base_parts.append(str(srcdir_relative_path))
except ValueError:
# If base_dir is not an ancestor directory of srcdir, default to srcdir.
# _logger.debug("PWD is not relative to base path.")
demo_uuid_base_parts.append(str(srcdir_resolved_path))
# Component: Command of argument vector.
env_venv_name = os.getenv("VIRTUAL_ENV")
if env_venv_name is None:
demo_uuid_base_parts.append(sys.argv[0])
else:
command_original_path = pathlib.Path(sys.argv[0])
command_resolved_path = command_original_path.resolve()
venv_original_path = pathlib.Path(env_venv_name)
venv_resolved_path = venv_original_path.resolve()
try:
command_relative_path = command_resolved_path.relative_to(
venv_resolved_path
)
# _logger.debug("command_relative_path = %r.", command_relative_path)
demo_uuid_base_parts.append(str(command_relative_path))
except ValueError:
# _logger.debug("Command path is not relative to virtual environment path.")
demo_uuid_base_parts.append(str(command_resolved_path))
if len(sys.argv) > 1:
# Component: Arguments of argument vector.
demo_uuid_base_parts.extend(sys.argv[1:])
# _logger.debug("demo_uuid_base_parts = %r.", demo_uuid_base_parts)
DEMO_UUID_BASE = "/".join(demo_uuid_base_parts)
def demo_uuid() -> str:
"""
This function generates a repeatable UUID, drawing on non-varying elements of the environment and process call for entropy.
WARNING: This function was developed for use ONLY for reducing (but not eliminating) version-control edits to identifiers when generating sample data. It creates UUIDs that are decidedly NOT random, and should remain consistent on repeated calls to the importing script.
To prevent accidental non-random UUID usage, an environment variable, CASE_DEMO_NONRANDOM_UUID_BASE, must be set to a string provided by the caller. The variable's required value is the path to some directory. The variable's recommended value is the equivalent of the Make variable "top_srcdir" - that is, the root directory of the containing Git repository, some parent of the current process's current working directory.
"""
global DEMO_UUID_BASE
global DEMO_UUID_COUNTER
if os.getenv("CASE_DEMO_NONRANDOM_UUID_BASE") is None:
raise ValueError(
"demo_uuid() called without CASE_DEMO_NONRANDOM_UUID_BASE in environment."
)
if DEMO_UUID_BASE is None:
raise ValueError("demo_uuid() called with DEMO_UUID_BASE unset.")
parts = [DEMO_UUID_BASE]
# Component: Incrementing counter.
DEMO_UUID_COUNTER += 1
parts.append(str(DEMO_UUID_COUNTER))
return str(uuid.uuid5(uuid.NAMESPACE_URL, "/".join(parts)))
def local_uuid() -> str:
"""
Generate either a UUID4, or if requested via environment configuration, a non-random demo UUID.
"""
global DEMO_UUID_BASE
if DEMO_UUID_BASE is None:
return str(uuid.uuid4())
else:
return demo_uuid()