Repository: pydantic/pydantic
Pull request: Add `rediss` (Redis over SSL) protocol to `RedisDsn`
## Change Summary

Added `rediss` to `RedisDsn.allowed_schemes`.

## Related issue number

This PR closes #1877 

## Checklist

* [x] Unit tests for the changes exist
* [x] Tests pass on CI and coverage remains at 100%
* [x] Documentation reflects the changes where applicable
* [x] `changes/1911-TrDex.md` file added describing change
  (see [changes/README.md](https://github.com/samuelcolvin/pydantic/blob/master/changes/README.md) for details)


Problem statement:
RedisDsn canot enable redis url with password and ssl schema
`redis://:password@redishost` & `rediss://redis` are valid in redis.py, but cause ValidationError in RedisDsn. 

```
In [7]: import redis

In [8]: r = redis.StrictRedis.from_url('rediss://redis')

In [9]: r = redis.StrictRedis.from_url('redis://:123@redis')

In [10]:
```

* type error list below
```
In [3]: from pydantic import BaseModel, RedisDsn

In [4]: class RedisURLModel(BaseModel):
   ...:     url: RedisDsn = None
   ...:

In [5]: c = RedisURLModel(url='redis://:myredispwd@redis')
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-5-23cad9c7fe6b> in <module>
----> 1 c = RedisURLModel(url='redis://:myredispwd@redis')

/usr/local/lib/python3.7/site-packages/pydantic/main.cpython-37m-x86_64-linux-gnu.so in pydantic.main.BaseModel.__init__()

ValidationError: 1 validation error for RedisURLModel
url
  URL host invalid (type=value_error.url.host)

In [6]: c = RedisURLModel(url='rediss://redis')
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-6-39558dddb3bc> in <module>
----> 1 c = RedisURLModel(url='rediss://redis')

/usr/local/lib/python3.7/site-packages/pydantic/main.cpython-37m-x86_64-linux-gnu.so in pydantic.main.BaseModel.__init__()

ValidationError: 1 validation error for RedisURLModel
url
  URL scheme not permitted (type=value_error.url.scheme; allowed_schemes={'redis'})
```

Hints from issue discussion:

Commit message: Allow URLs without `user` part

Refer to #1877

Patch to review:
diff --git a/changes/1911-TrDex.md b/changes/1911-TrDex.md
new file mode 100644
index 00000000000..0885b029e81
--- /dev/null
+++ b/changes/1911-TrDex.md
@@ -0,0 +1 @@
+Add `rediss` (Redis over SSL) protocol to `RedisDsn`
diff --git a/docs/usage/types.md b/docs/usage/types.md
index 9f30ab6f32a..740775a99ae 100644
--- a/docs/usage/types.md
+++ b/docs/usage/types.md
@@ -513,8 +513,8 @@ 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`, user info required, TLD not required
-- `RedisDsn`: schema `redis`, user info not required, tld not required (CHANGED: user info not required from
-  **v1.6** onwards)
+- `RedisDsn`: schema `redis` or `rediss`, user info not required, tld not required (CHANGED: user info
+  not required from **v1.6** onwards)
 - `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 2418e5b6c63..3d2ce295bab 100644
--- a/pydantic/networks.py
+++ b/pydantic/networks.py
@@ -54,7 +54,7 @@ def url_regex() -> Pattern[str]:
     if _url_regex_cache is None:
         _url_regex_cache = re.compile(
             r'(?:(?P<scheme>[a-z][a-z0-9+\-.]+)://)?'  # scheme https://tools.ietf.org/html/rfc3986#appendix-A
-            r'(?:(?P<user>[^\s:/]+)(?::(?P<password>[^\s/]*))?@)?'  # user info
+            r'(?:(?P<user>[^\s:/]*)(?::(?P<password>[^\s/]*))?@)?'  # user info
             r'(?:'
             r'(?P<ipv4>(?:\d{1,3}\.){3}\d{1,3})|'  # ipv4
             r'(?P<ipv6>\[[A-F0-9]*:[A-F0-9:]+\])|'  # ipv6
@@ -147,8 +147,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:
@@ -279,7 +280,7 @@ class PostgresDsn(AnyUrl):
 
 
 class RedisDsn(AnyUrl):
-    allowed_schemes = {'redis'}
+    allowed_schemes = {'redis', 'rediss'}
 
 
 def stricturl(
diff --git a/pydantic/utils.py b/pydantic/utils.py
index d692f4f53c6..d14a2858a3b 100644
--- a/pydantic/utils.py
+++ b/pydantic/utils.py
@@ -241,7 +241,8 @@ def unique_list(input_list: Union[List[T], Tuple[T, ...]]) -> List[T]:
 
 
 def update_normalized_all(
-    item: Union['AbstractSetIntStr', 'MappingIntStrAny'], all_items: Union['AbstractSetIntStr', 'MappingIntStrAny'],
+    item: Union['AbstractSetIntStr', 'MappingIntStrAny'],
+    all_items: Union['AbstractSetIntStr', 'MappingIntStrAny'],
 ) -> Union['AbstractSetIntStr', 'MappingIntStrAny']:
     """
     Update item based on what all items contains.
diff --git a/tests/requirements.txt b/tests/requirements.txt
index 1f9ef4e2619..b7f098bb9d1 100644
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -1,4 +1,4 @@
-black==19.10b0
+black==20.8b1
 coverage==5.2.1
 flake8==3.8.3
 flake8-quotes==3.2.0
diff --git a/tests/test_networks.py b/tests/test_networks.py
index c69ba0a7870..a1b4cde08eb 100644
--- a/tests/test_networks.py
+++ b/tests/test_networks.py
@@ -326,6 +326,12 @@ class Model(BaseModel):
     assert m.a.user == 'user'
     assert m.a.password == 'pass'
 
+    m = Model(a='rediss://user:pass@localhost:5432/app')
+    assert m.a == 'rediss://user:pass@localhost:5432/app'
+
+    m = Model(a='rediss://:pass@localhost')
+    assert m.a == 'rediss://:pass@localhost'
+
     with pytest.raises(ValidationError) as exc_info:
         Model(a='http://example.org')
     assert exc_info.value.errors()[0]['type'] == 'value_error.url.scheme'


Code review comments:
diff_hunk: @@ -1,4 +1,4 @@
-black==19.10b0
+black==20.8b1
path: tests/requirements.txt
text: @user1:
best not to change requirements in this PR.