diff --git a/changes/1275-kesavkolla.md b/changes/1275-kesavkolla.md new file mode 100644 index 0000000000..9b7374efb8 --- /dev/null +++ b/changes/1275-kesavkolla.md @@ -0,0 +1 @@ +Made user info optional for `RedisDSN` diff --git a/docs/examples/settings_main.py b/docs/examples/settings_main.py index 843940f47e..f49228f15e 100644 --- a/docs/examples/settings_main.py +++ b/docs/examples/settings_main.py @@ -12,7 +12,7 @@ class Settings(BaseSettings): auth_key: str api_key: str = Field(..., env='my_api_key') - redis_dsn: RedisDsn = 'redis://user:pass@localhost:6379/1' + redis_dsn: RedisDsn = 'redis://:pass@localhost:6379/1' pg_dsn: PostgresDsn = 'postgres://user:pass@localhost:5432/foobar' special_function: PyObject = 'math.cos' diff --git a/docs/usage/types.md b/docs/usage/types.md index 6e7dbddd67..72e7ed02bf 100644 --- a/docs/usage/types.md +++ b/docs/usage/types.md @@ -509,7 +509,7 @@ For URI/URL validation the following types are available: - `AnyHttpUrl`: schema `http` or `https`, TLD not required - `HttpUrl`: schema `http` or `https`, TLD required, max length 2083 - `PostgresDsn`: schema `postgres` or `postgresql`, userinfo required, TLD not required -- `RedisDsn`: schema `redis`, userinfo required, tld not required +- `RedisDsn`: schema `redis`, userinfo not required, tld not required - `stricturl`, method with the following keyword arguments: - `strip_whitespace: bool = True` - `min_length: int = 1` diff --git a/pydantic/networks.py b/pydantic/networks.py index 9f9aca253a..b399484a83 100644 --- a/pydantic/networks.py +++ b/pydantic/networks.py @@ -53,7 +53,7 @@ def url_regex() -> Pattern[str]: if _url_regex_cache is None: _url_regex_cache = re.compile( r'(?:(?P[a-z][a-z0-9+\-.]+)://)?' # scheme https://tools.ietf.org/html/rfc3986#appendix-A - r'(?:(?P[^\s:/]+)(?::(?P[^\s/]*))?@)?' # user info + r'(?:(?P[^\s:/]+)?(?::(?P[^\s/]*))?@)?' # user info r'(?:' r'(?P(?:\d{1,3}\.){3}\d{1,3})|' # ipv4 r'(?P\[[A-F0-9]*:[A-F0-9:]+\])|' # ipv6 @@ -146,8 +146,9 @@ def build( url = scheme + '://' if user: url += user - if password: - url += ':' + password + if password: + url += ':' + password + if user or password: url += '@' url += host if port: @@ -189,7 +190,8 @@ def validate(cls, value: Any, field: 'ModelField', config: 'BaseConfig') -> 'Any raise errors.UrlSchemePermittedError(cls.allowed_schemes) user = parts['user'] - if cls.user_required and user is None: + password = parts['password'] + if cls.user_required and user is None and password is None: raise errors.UrlUserInfoError() host, tld, host_type, rebuild = cls.validate_host(parts) @@ -201,7 +203,7 @@ def validate(cls, value: Any, field: 'ModelField', config: 'BaseConfig') -> 'Any None if rebuild else url, scheme=scheme, user=user, - password=parts['password'], + password=password, host=host, tld=tld, host_type=host_type, @@ -274,7 +276,7 @@ class PostgresDsn(AnyUrl): class RedisDsn(AnyUrl): allowed_schemes = {'redis'} - user_required = True + user_required = False def stricturl( diff --git a/tests/test_networks.py b/tests/test_networks.py index b078d7729c..c4c41eabe7 100644 --- a/tests/test_networks.py +++ b/tests/test_networks.py @@ -320,15 +320,14 @@ class Model(BaseModel): assert m.a.user == 'user' assert m.a.password == 'pass' + m = Model(a='redis://:pass@localhost:5432') + assert m.a == 'redis://:pass@localhost:5432' + assert m.a.password == 'pass' + with pytest.raises(ValidationError) as exc_info: Model(a='http://example.org') assert exc_info.value.errors()[0]['type'] == 'value_error.url.scheme' - with pytest.raises(ValidationError) as exc_info: - Model(a='redis://localhost:5432/app') - error = exc_info.value.errors()[0] - assert error == {'loc': ('a',), 'msg': 'userinfo required in URL but missing', 'type': 'value_error.url.userinfo'} - def test_custom_schemes(): class Model(BaseModel):