summaryrefslogtreecommitdiff
path: root/server/szurubooru/func/util.py
diff options
context:
space:
mode:
authorrr- <rr-@sakuya.pl>2017-02-04 01:08:12 +0100
committerrr- <rr-@sakuya.pl>2017-02-05 16:34:45 +0100
commitad842ee8a54c57463b8e28b52970f173ea1d64ea (patch)
tree1238e8270878801a2ddb10bc4db469115c026cab /server/szurubooru/func/util.py
parentabf1fc2b2d135299fe7e8a700d4e7a18966d7bfe (diff)
server: refactor + add type hinting
- Added type hinting (for now, 3.5-compatible) - Split `db` namespace into `db` module and `model` namespace - Changed elastic search to be created lazily for each operation - Changed to class based approach in entity serialization to allow stronger typing - Removed `required` argument from `context.get_*` family of functions; now it's implied if `default` argument is omitted - Changed `unalias_dict` implementation to use less magic inputs
Diffstat (limited to 'server/szurubooru/func/util.py')
-rw-r--r--server/szurubooru/func/util.py74
1 files changed, 30 insertions, 44 deletions
diff --git a/server/szurubooru/func/util.py b/server/szurubooru/func/util.py
index 11caedd..40d19d3 100644
--- a/server/szurubooru/func/util.py
+++ b/server/szurubooru/func/util.py
@@ -2,52 +2,39 @@ import os
import hashlib
import re
import tempfile
+from typing import (
+ Any, Optional, Union, Tuple, List, Dict, Generator, Callable, TypeVar)
from datetime import datetime, timedelta
from contextlib import contextmanager
from szurubooru import errors
-def snake_case_to_lower_camel_case(text):
+T = TypeVar('T')
+
+
+def snake_case_to_lower_camel_case(text: str) -> str:
components = text.split('_')
return components[0].lower() + \
''.join(word[0].upper() + word[1:].lower() for word in components[1:])
-def snake_case_to_upper_train_case(text):
+def snake_case_to_upper_train_case(text: str) -> str:
return '-'.join(
word[0].upper() + word[1:].lower() for word in text.split('_'))
-def snake_case_to_lower_camel_case_keys(source):
+def snake_case_to_lower_camel_case_keys(
+ source: Dict[str, Any]) -> Dict[str, Any]:
target = {}
for key, value in source.items():
target[snake_case_to_lower_camel_case(key)] = value
return target
-def get_serialization_options(ctx):
- return ctx.get_param_as_list('fields', required=False, default=None)
-
-
-def serialize_entity(entity, field_factories, options):
- if not entity:
- return None
- if not options or len(options) == 0:
- options = field_factories.keys()
- ret = {}
- for key in options:
- if key not in field_factories:
- raise errors.ValidationError('Invalid key: %r. Valid keys: %r.' % (
- key, list(sorted(field_factories.keys()))))
- factory = field_factories[key]
- ret[key] = factory()
- return ret
-
-
@contextmanager
-def create_temp_file(**kwargs):
- (handle, path) = tempfile.mkstemp(**kwargs)
- os.close(handle)
+def create_temp_file(**kwargs: Any) -> Generator:
+ (descriptor, path) = tempfile.mkstemp(**kwargs)
+ os.close(descriptor)
try:
with open(path, 'r+b') as handle:
yield handle
@@ -55,17 +42,15 @@ def create_temp_file(**kwargs):
os.remove(path)
-def unalias_dict(input_dict):
- output_dict = {}
- for key_list, value in input_dict.items():
- if isinstance(key_list, str):
- key_list = [key_list]
- for key in key_list:
- output_dict[key] = value
+def unalias_dict(source: List[Tuple[List[str], T]]) -> Dict[str, T]:
+ output_dict = {} # type: Dict[str, T]
+ for aliases, value in source:
+ for alias in aliases:
+ output_dict[alias] = value
return output_dict
-def get_md5(source):
+def get_md5(source: Union[str, bytes]) -> str:
if not isinstance(source, bytes):
source = source.encode('utf-8')
md5 = hashlib.md5()
@@ -73,7 +58,7 @@ def get_md5(source):
return md5.hexdigest()
-def get_sha1(source):
+def get_sha1(source: Union[str, bytes]) -> str:
if not isinstance(source, bytes):
source = source.encode('utf-8')
sha1 = hashlib.sha1()
@@ -81,24 +66,25 @@ def get_sha1(source):
return sha1.hexdigest()
-def flip(source):
+def flip(source: Dict[Any, Any]) -> Dict[Any, Any]:
return {v: k for k, v in source.items()}
-def is_valid_email(email):
+def is_valid_email(email: Optional[str]) -> bool:
''' Return whether given email address is valid or empty. '''
- return not email or re.match(r'^[^@]*@[^@]*\.[^@]*$', email)
+ return not email or re.match(r'^[^@]*@[^@]*\.[^@]*$', email) is not None
class dotdict(dict): # pylint: disable=invalid-name
''' dot.notation access to dictionary attributes. '''
- def __getattr__(self, attr):
+ def __getattr__(self, attr: str) -> Any:
return self.get(attr)
+
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
-def parse_time_range(value):
+def parse_time_range(value: str) -> Tuple[datetime, datetime]:
''' Return tuple containing min/max time for given text representation. '''
one_day = timedelta(days=1)
one_second = timedelta(seconds=1)
@@ -146,9 +132,9 @@ def parse_time_range(value):
raise errors.ValidationError('Invalid date format: %r.' % value)
-def icase_unique(source):
- target = []
- target_low = []
+def icase_unique(source: List[str]) -> List[str]:
+ target = [] # type: List[str]
+ target_low = [] # type: List[str]
for source_item in source:
if source_item.lower() not in target_low:
target.append(source_item)
@@ -156,7 +142,7 @@ def icase_unique(source):
return target
-def value_exceeds_column_size(value, column):
+def value_exceeds_column_size(value: Optional[str], column: Any) -> bool:
if not value:
return False
max_length = column.property.columns[0].type.length
@@ -165,6 +151,6 @@ def value_exceeds_column_size(value, column):
return len(value) > max_length
-def chunks(source_list, part_size):
+def chunks(source_list: List[Any], part_size: int) -> Generator:
for i in range(0, len(source_list), part_size):
yield source_list[i:i + part_size]