Coverage for src/edwh_restic_plugin/env.py: 16%
64 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-28 16:28 +0100
« prev ^ index » next coverage.py v7.6.12, created at 2025-02-28 16:28 +0100
1import os
2import warnings
3from pathlib import Path
4from typing import Optional
6# the path where the environment variables are going
7DOTENV = Path(".env")
8DOTENV.touch(exist_ok=True)
10_dotenv_settings: dict[Path, dict[str, str]] = {}
13def read_dotenv(path: Optional[Path] = None) -> dict[str, str]:
14 """Reads a .env file at the specified path and returns a dictionary of key - value pairs.
16 If the specified key is not found in the.env file, the function prompts the user to enter a value for the key,
17 with a default value provided.The key-value pair is then appended to the.env file.
19 Args:
20 path(Path): The path to the .env file.
22 Returns:
23 dict: A dictionary containing the key - value pairs in the .env file."""
24 path = path or DOTENV
26 if existing := _dotenv_settings.get(path):
27 # 'cache'
28 return existing
30 items = {}
31 with path.open(mode="r") as env_file:
32 for line in env_file:
33 # remove comments and redundant whitespace
34 line = line.split("#", 1)[0].strip()
35 if not line or "=" not in line:
36 # just a comment, skip
37 # or key without value? invalid, prevent crash:
38 continue
40 # convert to tuples
41 k, v = line.split("=", 1)
43 # clean the tuples and add to dict
44 items[k.strip()] = v.strip()
46 _dotenv_settings[path] = items
47 return items
50def set_env_value(path: Path, target: str, value: str) -> None:
51 """
52 update/set environment variables in the .env file, keeping comments intact
54 set_env_value(Path('.env'), 'SCHEMA_VERSION', schemaversion)
56 Args:
57 path: pathlib.Path designating the .env file
58 target: key to write, probably best to use UPPERCASE
59 value: string value to write, or anything that converts to a string using str()
60 """
61 with path.open(mode="r") as env_file:
62 # open the .env file and read every line in the inlines
63 inlines = env_file.read().split("\n")
65 outlines = [] # lines for output
66 geschreven = False
67 for line in inlines:
68 if line.strip().startswith("#"):
69 # ignore comments
70 outlines.append(line)
71 continue
72 # remove redundant whitespace
73 line = line.strip()
74 if not line:
75 # remove empty lines
76 continue
77 # convert to tuples
78 key, oldvalue = line.split("=", 1)
79 # clean the key and value
80 key = key.strip()
81 if key == target:
82 # add the new tuple to the lines
83 outlines.append(f"{key}={value}")
84 geschreven = True
85 else:
86 # or leave it as it is
87 outlines.append(line)
88 if not geschreven:
89 # if target in .env file
90 outlines.append(f"{target.strip().upper()}={value.strip()}")
91 with path.open(mode="w") as env_file:
92 # write outlines to .env file
93 env_file.write("\n".join(outlines))
94 env_file.write("\n")
97def check_env(
98 key: str,
99 default: str | None,
100 comment: str,
101 prefix: str = None,
102 suffix: str = None,
103 postfix: str = None,
104 path: Path = None,
105):
106 """
107 Test if key is in .env file path, appends prompted or default value if missing.
108 """
109 path = path or DOTENV
111 if postfix:
112 warnings.warn("`postfix` option passed. Please use `suffix` instead!", category=DeprecationWarning)
114 suffix = suffix or postfix
116 env = read_dotenv(path)
117 if key in env:
118 return env[key]
120 # get response value from prompt/input
121 # if response_value is empty make value default else value is response_value
122 value = input(f"Enter value for {key} ({comment})\n default=`{default}`: ").strip() or default or ""
123 if value.startswith("~/") and Path(value).expanduser().exists():
124 value = str(Path(value).expanduser())
125 if prefix:
126 value = prefix + value
127 if suffix:
128 value += suffix
130 with path.open(mode="r+") as env_file:
131 env_file.seek(0, 2)
132 # write key and value to .env file
133 env_file.write(f"\n{key.upper()}={value}")
135 # update in memory too:
136 os.environ[key] = env[key] = value
137 return value