password-validator-s 1.0.0__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.
Files changed (38) hide show
  1. password_validator/__init__.py +30 -0
  2. password_validator/config/__init__.py +0 -0
  3. password_validator/config/settings.py +135 -0
  4. password_validator/constants.py +55 -0
  5. password_validator/engine/__init__.py +0 -0
  6. password_validator/engine/validator.py +168 -0
  7. password_validator/enums.py +72 -0
  8. password_validator/exceptions.py +50 -0
  9. password_validator/loaders/__init__.py +10 -0
  10. password_validator/loaders/env_loader.py +149 -0
  11. password_validator/models.py +261 -0
  12. password_validator/rules/__init__.py +24 -0
  13. password_validator/rules/base.py +91 -0
  14. password_validator/rules/digits.py +35 -0
  15. password_validator/rules/length.py +43 -0
  16. password_validator/rules/lowercase.py +34 -0
  17. password_validator/rules/registry.py +81 -0
  18. password_validator/rules/special.py +48 -0
  19. password_validator/rules/uppercase.py +34 -0
  20. password_validator/strength/__init__.py +8 -0
  21. password_validator/strength/analyzer.py +101 -0
  22. password_validator/strength/analyzers/__init__.py +55 -0
  23. password_validator/strength/analyzers/dictionary.py +347 -0
  24. password_validator/strength/analyzers/keyboard.py +378 -0
  25. password_validator/strength/analyzers/repeat.py +311 -0
  26. password_validator/strength/analyzers/sequential.py +281 -0
  27. password_validator/strength/config.py +120 -0
  28. password_validator/strength/scorer.py +473 -0
  29. password_validator/strength/suggestions.py +224 -0
  30. password_validator/strength/weights.py +65 -0
  31. password_validator/version.py +7 -0
  32. password_validator_s-1.0.0.dist-info/METADATA +245 -0
  33. password_validator_s-1.0.0.dist-info/RECORD +38 -0
  34. password_validator_s-1.0.0.dist-info/WHEEL +5 -0
  35. password_validator_s-1.0.0.dist-info/licenses/LICENSE +19 -0
  36. password_validator_s-1.0.0.dist-info/top_level.txt +2 -0
  37. plugins/__init__.py +0 -0
  38. plugins/manager.py +4 -0
@@ -0,0 +1,30 @@
1
+ """
2
+ Entry point for the password_validator package.
3
+ A configurable password validation library that allows users to define custom rules and
4
+ criteria for password strength and security.
5
+ """
6
+ from .version import (
7
+ __title__,
8
+ __version__,
9
+ __author__,
10
+ __description__
11
+ )
12
+ from .engine.validator import PasswordValidator, ValidationResult
13
+ from .strength.scorer import PasswordStrengthScorer
14
+
15
+ # Package metadata
16
+ VERSION = __version__
17
+
18
+ __all__ = [
19
+ # Package Metadata
20
+ "__version__",
21
+ "__title__",
22
+ "__author__",
23
+ "__description__",
24
+ "VERSION",
25
+
26
+ # Main API
27
+ "PasswordValidator",
28
+ "ValidationResult",
29
+ "PasswordStrengthScorer",
30
+ ]
File without changes
@@ -0,0 +1,135 @@
1
+ """
2
+ Application-level configuration for password-validator.
3
+ This module provides the top-level package configuration.
4
+ Strength-specific configuration is maintained separately in:
5
+ password_validator.strength.config.StrengthConfig
6
+ Password policy configuration is maintained here and can be
7
+ loaded from environment variables.
8
+ """
9
+ from __future__ import annotations
10
+ from dataclasses import dataclass
11
+
12
+ from ..loaders.env_loader import EnvLoader
13
+ from ..strength.config import StrengthConfig
14
+ from password_validator.constants import *
15
+
16
+
17
+ @dataclass(frozen=True, slots=True)
18
+ class PasswordRuleConfig:
19
+ """
20
+ Configuration for password policy rules.
21
+ These settings determine whether a password satisfies the configured password policy.
22
+ This is separate from password strength scoring.
23
+ """
24
+ min_length: int = 8
25
+ max_length: int = 128
26
+
27
+ require_uppercase: bool = True
28
+ require_lowercase: bool =True
29
+ require_digit: bool =True
30
+ require_special: bool = True
31
+ special_characters: str = "!@#$%^&*()-_=[]{}|/:;'<>?"
32
+
33
+ """
34
+ require_whitespace: bool
35
+ min_uppercase: int
36
+ min_lowercase: int
37
+ min_digit: int
38
+ min_special: int
39
+
40
+ allowed_special: str
41
+ allow_spaces: bool
42
+
43
+ max_repeat: int
44
+ check_sequential: bool
45
+ check_common_passwords: bool
46
+ check_dictionary: bool
47
+
48
+ custom_regex: str | None
49
+ min_entropy: int
50
+ language: str
51
+ """
52
+
53
+ @classmethod
54
+ def from_env(cls, env: EnvLoader) -> "PasswordRuleConfig":
55
+ """
56
+ Loads the password policy configuration from EnvLoader.
57
+ :param env: .env file. Defaults to ".env".
58
+ :return: password policy object
59
+ """
60
+
61
+ return cls(
62
+ min_length=env.get_int("PASSWORD_MIN_LENGTH", DEFAULT_MIN_LENGTH),
63
+ max_length=env.get_int("PASSWORD_MAX_LENGTH", DEFAULT_MAX_LENGTH),
64
+
65
+ require_uppercase=env.get_bool("PASSWORD_REQUIRE_UPPERCASE", DEFAULT_REQUIRE_UPPERCASE),
66
+ require_lowercase=env.get_bool("PASSWORD_REQUIRE_LOWERCASE", DEFAULT_REQUIRE_LOWERCASE),
67
+ require_digit=env.get_bool("PASSWORD_REQUIRE_DIGIT", DEFAULT_REQUIRE_DIGIT),
68
+ require_special=env.get_bool("PASSWORD_REQUIRE_SPECIAL", DEFAULT_REQUIRE_SPECIAL),
69
+ special_characters=env.get("PASSWORD_SPECIAL_CHARACTERS", DEFAULT_SPECIAL_CHARACTERS)
70
+ )
71
+
72
+ def validate(self) -> None:
73
+ """
74
+ Validate the configuration itself.
75
+ Raises:
76
+ ValueError: If the configuration is invalid.
77
+ :return:
78
+ """
79
+ if self.min_length < 1:
80
+ raise ValueError("PASSWORD_MIN_LENGTH must be greater than 0")
81
+
82
+ if self.max_length < self.min_length:
83
+ raise ValueError("PASSWORD_MAX_LENGTH must be greater than or equal to PASSWORD_MIN_LENGTH")
84
+
85
+ if self.require_special and not self.special_characters:
86
+ raise ValueError("PASSWORD_SPECIAL_CHARACTERS cannot be empty when PASSWORD_REQUIRE_SPECIAL=true")
87
+
88
+ @classmethod
89
+ def defaults(cls) -> "PasswordRuleConfig":
90
+ """
91
+ Return the package defaults.
92
+ Useful for applications that don't use .env.
93
+ :return:
94
+ """
95
+ return cls()
96
+
97
+
98
+ @dataclass(slots=True, frozen=True)
99
+ class Settings:
100
+ """
101
+ Root configuration object for the password-validator package.
102
+ This object is the main configuration boundary for the package.
103
+
104
+ Environment variables are loaded once and converted into typed configuration objects.
105
+ """
106
+ rules: PasswordRuleConfig
107
+ strength: StrengthConfig
108
+
109
+ @classmethod
110
+ def from_env(cls, env_file: str = ".env") -> "Settings":
111
+ """
112
+ Build application settings from environment file.
113
+ :param env_file: Path to the .env file
114
+ :return: Fully initialized settings object
115
+ """
116
+ env = EnvLoader(env_file)
117
+ rules = PasswordRuleConfig.from_env(env)
118
+ rules.validate()
119
+ strength = StrengthConfig.from_env(env_file)
120
+
121
+ return cls(rules=rules, strength=strength)
122
+
123
+ @classmethod
124
+ def defaults(cls) -> "Settings":
125
+ """
126
+ Return package defaults without reading an environment file
127
+ :return:
128
+ """
129
+ rules = PasswordRuleConfig()
130
+ rules.validate()
131
+
132
+ return cls(
133
+ rules=rules,
134
+ strength=StrengthConfig.defaults()
135
+ )
@@ -0,0 +1,55 @@
1
+ """
2
+ All constants used in the password_validator package are defined here.
3
+ - DEFAULT_SPECIAL_CHARACTERS
4
+ - DEFAULT_BLACKLIST
5
+ - DEFAULT_MIN_LENGTH
6
+ - DEFAULT_ENTROPY
7
+ """
8
+ # Password defaults
9
+ DEFAULT_MIN_LENGTH = 8
10
+ DEFAULT_MAX_LENGTH = 64
11
+
12
+ DEFAULT_REQUIRE_UPPERCASE = True
13
+ DEFAULT_REQUIRE_LOWERCASE = True
14
+ DEFAULT_REQUIRE_DIGIT = True
15
+ DEFAULT_REQUIRE_SPECIAL = True
16
+
17
+ # Character defaults
18
+ DEFAULT_SPECIAL_CHARACTERS = "!@#$%^&*()-_=+[]{}|;:'\",.<>?/`~"
19
+
20
+ DEFAULT_MIN_UPPERCASE = 1
21
+ DEFAULT_MIN_LOWERCASE = 1
22
+ DEFAULT_MIN_DIGITS = 1
23
+ DEFAULT_MIN_SPECIAL = 1
24
+
25
+ # Security defaults
26
+ DEFAULT_MAX_REPEAT_COUNT = 3
27
+ DEFAULT_MIN_ENTROPY = 40
28
+
29
+ # Password policies
30
+ DEFAULT_ALLOW_SPACES = False
31
+ DEFAULT_CHECK_SEQUENTIAL = True
32
+ DEFAULT_CHECK_COMMON_PASSWORDS = True
33
+ DEFAULT_CHECK_DICTIONARY = True
34
+
35
+ # Regex
36
+ DEFAULT_CUSTOM_REGEX = None
37
+
38
+ # Language defaults
39
+ DEFAULT_LANGUAGE = "en"
40
+
41
+ # Common sequences and patterns
42
+ SEQUENTIAL_PATTERNS = [
43
+ "abcdefghijklmnopqrstuvwxyz",
44
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
45
+ "0123456789"
46
+ ]
47
+
48
+ # Common password file
49
+ DEFAULT_COMMON_PASSWORDS_FILE = "resources/common_passwords.txt"
50
+ DEFAULT_DICTIONARY_FILE = "resources/dictionary.txt"
51
+
52
+ # Error messages
53
+ DEFAULT_ERROR_PREFIX = (
54
+ "Password validation failed"
55
+ )
File without changes
@@ -0,0 +1,168 @@
1
+ """
2
+ Password validation engine.
3
+
4
+ The validation engine executes password policy rules and returns a
5
+ structured validation result.
6
+
7
+ It does not perform password strength analysis. Strength analysis is
8
+ handled by password_validator.strength.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from collections.abc import Sequence
14
+
15
+ from ..config.settings import PasswordRuleConfig
16
+ from ..models import ValidationResult
17
+ from ..rules.base import Rule, RuleResult
18
+ from ..rules.digits import DigitsRule
19
+ from ..rules.length import LengthRule
20
+ from ..rules.lowercase import LowercaseRule
21
+ from ..rules.special import SpecialCharacterRule
22
+ from ..rules.uppercase import UppercaseRule
23
+
24
+
25
+ class PasswordValidator:
26
+ """
27
+ Password policy validation engine.
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ config: PasswordRuleConfig | None = None,
33
+ rules: Sequence[Rule] | None = None,
34
+ ) -> None:
35
+ """
36
+ Initialize the password validator.
37
+
38
+ Args:
39
+ config: Password policy configuration.
40
+ rules: Optional custom rule collection.
41
+
42
+ When rules are supplied, they completely replace the default
43
+ rule collection.
44
+ """
45
+
46
+ self.config = config or PasswordRuleConfig()
47
+
48
+ if hasattr(self.config, "validate"):
49
+ self.config.validate()
50
+
51
+ if rules is not None:
52
+ self.rules = list(rules)
53
+ else:
54
+ self.rules = self._build_default_rules()
55
+
56
+ def _build_default_rules(self) -> list[Rule]:
57
+ """Build the default password policy rule collection."""
58
+
59
+ rules: list[Rule] = []
60
+
61
+ rules.append(
62
+ LengthRule(
63
+ min_length=self.config.min_length,
64
+ max_length=self.config.max_length,
65
+ )
66
+ )
67
+
68
+ if self.config.require_uppercase:
69
+ rules.append(UppercaseRule())
70
+
71
+ if self.config.require_lowercase:
72
+ rules.append(LowercaseRule())
73
+
74
+ if self.config.require_digit:
75
+ rules.append(DigitsRule())
76
+
77
+ if self.config.require_special:
78
+ rules.append(
79
+ SpecialCharacterRule(
80
+ special_characters=self.config.special_characters,
81
+ )
82
+ )
83
+
84
+ return rules
85
+
86
+ def validate(self, password: str) -> ValidationResult:
87
+ """
88
+ Validate a password against all configured rules.
89
+
90
+ Args:
91
+ password: Password to validate.
92
+
93
+ Returns:
94
+ ValidationResult containing the overall status and individual
95
+ rule results.
96
+
97
+ Raises:
98
+ TypeError: If password is not a string.
99
+ """
100
+
101
+ if not isinstance(password, str):
102
+ raise TypeError("Password must be a string")
103
+
104
+ rule_results: list[RuleResult] = []
105
+ passed: list[str] = []
106
+ failed: list[str] = []
107
+ errors: list[str] = []
108
+
109
+ for rule in self.rules:
110
+ result = rule.validate(password)
111
+
112
+ rule_results.append(result)
113
+
114
+ rule_name = self._rule_name(rule, result)
115
+
116
+ if result.passed:
117
+ passed.append(rule_name)
118
+ else:
119
+ failed.append(rule_name)
120
+
121
+ if result.message:
122
+ errors.append(result.message)
123
+
124
+ return ValidationResult(
125
+ valid=not failed,
126
+ passed=tuple(passed),
127
+ failed=tuple(failed),
128
+ errors=tuple(errors),
129
+ rule_results=tuple(rule_results),
130
+ )
131
+
132
+ @staticmethod
133
+ def _rule_name(
134
+ rule: Rule,
135
+ result: RuleResult,
136
+ ) -> str:
137
+ """
138
+ Resolve a stable rule name.
139
+
140
+ Resolution order:
141
+
142
+ 1. RuleResult.rule_name
143
+ 2. rule.name
144
+ 3. Rule class name with the "Rule" suffix removed
145
+ """
146
+
147
+ result_name = getattr(result, "rule_name", None)
148
+
149
+ if result_name:
150
+ return str(result_name)
151
+
152
+ rule_name = getattr(rule, "name", None)
153
+
154
+ if rule_name:
155
+ return str(rule_name)
156
+
157
+ class_name = rule.__class__.__name__
158
+
159
+ if class_name.endswith("Rule"):
160
+ class_name = class_name[:-4]
161
+
162
+ return class_name.lower()
163
+
164
+
165
+ __all__ = [
166
+ "PasswordValidator",
167
+ "ValidationResult",
168
+ ]
@@ -0,0 +1,72 @@
1
+ """
2
+ Enums used in the password_validator package are defined here.
3
+ - Strength
4
+ - RuleType
5
+ - ErrorCode
6
+ """
7
+ from enum import Enum
8
+
9
+
10
+ class Rule(str, Enum):
11
+ """
12
+ Supported validation rule types for password validation.
13
+ """
14
+
15
+ LENGTH = "length"
16
+ UPPERCASE = "uppercase"
17
+ LOWERCASE = "lowercase"
18
+ DIGIT = "digit"
19
+ SPECIAL = "special"
20
+ WHITESPACE = "whitespace"
21
+ REPEATED = "repeated"
22
+ SEQUENTIAL = "sequential"
23
+ BLACKLIST = "blacklist"
24
+ COMMON_PASSWORDS = "common_passwords"
25
+ DICTIONARY = "dictionary"
26
+ REGEX = "regex"
27
+ USERNAME = "username"
28
+ ENTROPY = "entropy"
29
+ HISTORY = "history"
30
+
31
+
32
+ class StrengthLevel(str, Enum):
33
+ """
34
+ Supported strength levels for password validation.
35
+ """
36
+ VERY_WEAK = "very_weak"
37
+ WEAK = "weak"
38
+ FAIR = "fair"
39
+ GOOD = "good"
40
+ STRONG = "strong"
41
+ VERY_STRONG = "very_strong"
42
+
43
+
44
+ class ErrorCode(str, Enum):
45
+ """
46
+ Supported error codes for password validation.
47
+ """
48
+ TOO_SHORT = "PASSWORD_TOO_SHORT"
49
+ TOO_LONG = "PASSWORD_TOO_LONG"
50
+ MISSING_UPPERCASE = "PASSWORD_MISSING_UPPERCASE"
51
+ MISSING_LOWERCASE = "PASSWORD_MISSING_LOWERCASE"
52
+ MISSING_DIGIT = "PASSWORD_MISSING_DIGIT"
53
+ MISSING_SPECIAL = "PASSWORD_MISSING_SPECIAL"
54
+ CONTAINS_SPACE = "PASSWORD_CONTAINS_SPACE"
55
+ TOO_MANY_REPEATS = "PASSWORD_TOO_MANY_REPEATS"
56
+ SEQUENTIAL_PATTERN = "PASSWORD_SEQUENTIAL_PATTERN"
57
+ COMMON_PASSWORD = "PASSWORD_COMMON_PASSWORD"
58
+ DICTIONARY_WORD = "PASSWORD_DICTIONARY_WORD"
59
+ INVALID_REGEX = "PASSWORD_INVALID_REGEX"
60
+ USERNAME_IN_PASSWORD = "USERNAME_IN_PASSWORD"
61
+ LOW_ENTROPY = "PASSWORD_LOW_ENTROPY"
62
+ PASSWORD_HISTORY_VIOLATION = "PASSWORD_HISTORY_VIOLATION"
63
+ BLACKLISTED_PASSWORD = "BLACKLISTED_PASSWORD"
64
+
65
+
66
+ class ValidationMode(str, Enum):
67
+ """
68
+ Supported validation modes for password validation.
69
+ """
70
+ STRICT = "strict"
71
+ NORMAL = "normal"
72
+ LENIENT = "lenient"
@@ -0,0 +1,50 @@
1
+ """
2
+ All exceptions used in the password_validator package are defined here.
3
+ - PasswordValidationError
4
+ - ConfigurationError
5
+ - RuleConfigurationError
6
+ """
7
+
8
+
9
+ class PasswordValidatorError(Exception):
10
+ """Raised when a password fails validation."""
11
+ pass
12
+
13
+
14
+ class ConfigurationError(PasswordValidatorError):
15
+ """Raised when there is an issue with the configuration."""
16
+ pass
17
+
18
+
19
+ class MissingConfigurationError(ConfigurationError):
20
+ """Raised when a required configuration is missing."""
21
+ pass
22
+
23
+
24
+ class RuleConfigurationError(ConfigurationError):
25
+ """Raised when there is an issue with a specific rule's configuration."""
26
+ pass
27
+
28
+
29
+ class InvalidConfigurationValue(PasswordValidatorError):
30
+ """Raised when a configuration value is invalid."""
31
+ """
32
+ Raised when an environment/configuration value
33
+ has an invalid type or format.
34
+ """
35
+
36
+ def __init__(
37
+ self,
38
+ key: str,
39
+ value: object,
40
+ expected: str,
41
+ ) -> None:
42
+ super().__init__(
43
+ f"Invalid configuration value for "
44
+ f"'{key}': {value!r}. "
45
+ f"Expected {expected}."
46
+ )
47
+
48
+ self.key = key
49
+ self.value = value
50
+ self.expected = expected
@@ -0,0 +1,10 @@
1
+ """
2
+ Configuration loaders
3
+ """
4
+
5
+ from .env_loader import EnvLoader
6
+
7
+
8
+ __all__ = [
9
+ "EnvLoader"
10
+ ]
@@ -0,0 +1,149 @@
1
+ """
2
+ Environment configuration loader.
3
+ """
4
+ from __future__ import annotations
5
+
6
+ import os
7
+ from pathlib import Path
8
+ from ..exceptions import InvalidConfigurationValue
9
+
10
+
11
+ class EnvLoader:
12
+ """
13
+ Loads and converts environment variables into a configuration dictionary.
14
+ """
15
+ def __init__(self, env_file: str | None = ".env") -> None:
16
+ """
17
+ Initializes the EnvLoader.
18
+
19
+ :param env_file: Path to the .env file. Defaults to ".env".
20
+ """
21
+ self._values: dict[str, str] = {}
22
+
23
+ if env_file:
24
+ self._load_env_file(env_file)
25
+
26
+ self._load_environment()
27
+
28
+ def _load_env_file(self, env_file: str) -> None:
29
+ path = Path(env_file)
30
+
31
+ if not path.exists():
32
+ return
33
+
34
+ for raw_line in path.read_text(encoding="utf-8").splitlines():
35
+ line = raw_line.strip()
36
+
37
+ if not line:
38
+ continue
39
+ if line.startswith("#"):
40
+ continue
41
+ if "=" not in line:
42
+ continue
43
+
44
+ key, value = line.split("=", 1)
45
+
46
+ key = key.strip()
47
+ value = value.strip()
48
+
49
+ if (len(value) >= 2 and
50
+ value[0] == value[-1] and
51
+ value[0] in {'"', "'"}):
52
+ value = value[1:-1]
53
+
54
+ self._values[key] = value
55
+
56
+ def _load_environment(self) -> None:
57
+ """
58
+ Environment variables override values from .env
59
+ :return:
60
+ """
61
+ for key, value in os.environ.items():
62
+ self._values[key] = value
63
+
64
+ def get(self, key: str, default: str = None) -> str:
65
+ """
66
+ Retrieves the value of an environment variable.
67
+
68
+ :param key: The environment variable key.
69
+ :param default: The default value if the key is not found.
70
+ :return: The value of the environment variable or the default.
71
+ """
72
+ return self._values.get(key, default)
73
+
74
+ def get_bool(self, key: str, default: bool = False) -> bool:
75
+ """
76
+ Retrieves the value of an environment variable as a boolean.
77
+
78
+ :param key: The environment variable key.
79
+ :param default: The default boolean value if the key is not found.
80
+ :return: The boolean value of the environment variable or the default.
81
+ """
82
+ value = self.get(key)
83
+
84
+ if value is None:
85
+ return default
86
+
87
+ normalized = value.strip().lower()
88
+
89
+ if normalized in ['true', '1', 'yes', 'y', "on"]:
90
+ return True
91
+
92
+ if value.lower() in ['false', '0', 'no', 'n', "off"]:
93
+ return False
94
+
95
+ raise InvalidConfigurationValue(f"Invalid boolean value for {key}: {value}")
96
+
97
+ def get_int(self, key: str, default: int = 0) -> int:
98
+ """
99
+ Retrieves the value of an environment variable as an integer.
100
+
101
+ :param key: The environment variable key.
102
+ :param default: The default integer value if the key is not found.
103
+ :return: The integer value of the environment variable or the default.
104
+ """
105
+ value = self.get(key)
106
+
107
+ if value is None:
108
+ return default
109
+
110
+ try:
111
+ return int(value)
112
+ except ValueError:
113
+ raise InvalidConfigurationValue(key, value, "integer") from None
114
+
115
+ def get_list(self, key: str, default: list[str] = None, delimiter: str = ',') -> list[str]:
116
+ """
117
+ Retrieves the value of an environment variable as a list.
118
+
119
+ :param key: The environment variable key.
120
+ :param default: The default list value if the key is not found.
121
+ :param delimiter: The delimiter used to split the string into a list. Defaults to ','.
122
+ :return: The list value of the environment variable or the default.
123
+ """
124
+ value = self.get(key, default)
125
+
126
+ if isinstance(value, list):
127
+ return value
128
+
129
+ if isinstance(value, str):
130
+ return [item.strip() for item in value.split(delimiter) if item.strip()]
131
+
132
+ raise InvalidConfigurationValue(f"{key}: {value} must be a list or a string")
133
+
134
+ def get_float(self, key: str, default: float = 0.0) -> float:
135
+ """
136
+ Retrieves the value of an environment variable as a float.
137
+
138
+ :param key: The environment variable key.
139
+ :param default: The default float value if the key is not found.
140
+ :return: The float value of the environment variable or the default.
141
+ """
142
+ value = self.get(key)
143
+ if value is None:
144
+ return default
145
+
146
+ try:
147
+ return float(value)
148
+ except (TypeError, ValueError) as exc:
149
+ raise InvalidConfigurationValue(key, value, "float") from None