Repository: horejsek/python-fastjsonschema

Issue:
'example.de' (and many others hostnames) not valid with hostname format 
Aaand another one. Hope you aren't getting annoyed. Running this example throws a JsonSchemaException:

```
import fastjsonschema

schema = {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$id": "https://example.com/example.schema.json",
    "title": "Example",
    "description": "An example schema",
    "type": "object",
    "properties": {
        "host": {
            "type": "string",
            "description": "Some hostname",
            "format": "hostname"
        }   
    }
}

validate = fastjsonschema.compile(schema)

validate({"host": "example.de"})
```

For 'google.com' it works fine. I'll create a PR soon, probably.

Comments:

Patch:
diff --git a/fastjsonschema/draft04.py b/fastjsonschema/draft04.py
index 6d79a1e..57b2a85 100644
--- a/fastjsonschema/draft04.py
+++ b/fastjsonschema/draft04.py
@@ -20,10 +20,7 @@ class CodeGeneratorDraft04(CodeGenerator):
     FORMAT_REGEXS = {
         'date-time': r'^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:[+-][0-2]\d:[0-5]\d|Z)?$',
         'email': r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$',
-        'hostname': (
-            r'^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*'
-            r'([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]{1,62}[A-Za-z0-9])$'
-        ),
+        'hostname': r'^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]{0,61}[A-Za-z0-9])$',
         'ipv4': r'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$',
         'ipv6': r'^(?:(?:[0-9A-Fa-f]{1,4}:){6}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|::(?:[0-9A-Fa-f]{1,4}:){5}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){4}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){3}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){,2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){,4}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|(?:(?:[0-9A-Fa-f]{1,4}:){,6}[0-9A-Fa-f]{1,4})?::)$',
         'uri': r'^\w+:(\/?\/?)[^\s]+$',


Test patch:
diff --git a/tests/test_datetime.py b/tests/test_datetime.py
index 2263e9f..b9e460b 100644
--- a/tests/test_datetime.py
+++ b/tests/test_datetime.py
@@ -1,4 +1,3 @@
-
 import pytest
 
 from fastjsonschema import JsonSchemaException
diff --git a/tests/test_hostname.py b/tests/test_hostname.py
new file mode 100644
index 0000000..8620ed1
--- /dev/null
+++ b/tests/test_hostname.py
@@ -0,0 +1,19 @@
+import pytest
+
+from fastjsonschema import JsonSchemaException
+
+
+exc = JsonSchemaException('data must be hostname')
+@pytest.mark.parametrize('value, expected', [
+    ('', exc),
+    ('LDhsjf878&d', exc),
+    ('bla.bla-', exc),
+    ('example.example.com-', exc),
+    ('localhost', 'localhost'),
+    ('example.com', 'example.com'),
+    ('example.de', 'example.de'),
+    ('example.fr', 'example.fr'),
+    ('example.example.com', 'example.example.com'),
+])
+def test_hostname(asserter, value, expected):
+    asserter({'type': 'string', 'format': 'hostname'}, value, expected)