1# -*- coding: utf-8 -*- 2from __future__ import absolute_import 3from __future__ import division 4from __future__ import print_function 5from __future__ import unicode_literals 6 7import os 8import io 9import sys 10import six 11import json 12import requests 13from copy import deepcopy 14from importlib import import_module 15from . import exceptions 16from . import config 17 18 19# Retrieve descriptor 20 21def retrieve_descriptor(source): 22 23 try: 24 # Inline 25 if isinstance(source, (dict, list)): 26 return deepcopy(source) 27 28 # String 29 if isinstance(source, six.string_types): 30 # Remote 31 if six.moves.urllib.parse.urlparse(source).scheme in config.REMOTE_SCHEMES: 32 return requests.get(source).json() 33 34 # Local 35 with io.open(source, encoding='utf-8') as file: 36 return json.load(file) 37 38 # Stream 39 return json.load(source) 40 41 except Exception: 42 raise exceptions.LoadError('Can\'t load descriptor') 43 44 45# Expand descriptor 46 47def expand_schema_descriptor(descriptor): 48 if isinstance(descriptor, dict): 49 descriptor = deepcopy(descriptor) 50 for field in descriptor.get('fields', []): 51 field.setdefault('type', config.DEFAULT_FIELD_TYPE) 52 field.setdefault('format', config.DEFAULT_FIELD_FORMAT) 53 descriptor.setdefault('missingValues', config.DEFAULT_MISSING_VALUES) 54 return descriptor 55 56 57def expand_field_descriptor(descriptor): 58 descriptor = deepcopy(descriptor) 59 descriptor.setdefault('type', config.DEFAULT_FIELD_TYPE) 60 descriptor.setdefault('format', config.DEFAULT_FIELD_FORMAT) 61 return descriptor 62 63 64# Miscellaneous 65 66def ensure_dir(path): 67 """Ensure directory exists. 68 69 Args: 70 path(str): dir path 71 72 """ 73 dirpath = os.path.dirname(path) 74 if dirpath and not os.path.exists(dirpath): 75 os.makedirs(dirpath) 76 77 78def normalize_value(value): 79 """Convert value to string and make it lower cased. 80 """ 81 cast = str 82 if six.PY2: 83 cast = unicode # noqa 84 return cast(value).lower() 85 86 87def default_exc_handler(exc, *args, **kwargs): 88 """Default exception handler function: raise exc, ignore other arguments. 89 """ 90 raise exc 91 92 93class PluginImporter(object): 94 """Plugin importer. 95 96 Example: 97 Add to myapp.plugins something like this: 98 ``` 99 importer = PluginImporter(virtual='myapp.plugins.', actual='myapp_') 100 importer.register() 101 del PluginImporter 102 del importer 103 ``` 104 105 """ 106 107 # Public 108 109 def __init__(self, virtual, actual): 110 self.__virtual = virtual 111 self.__actual = actual 112 113 def __eq__(self, other): 114 if not isinstance(other, type(self)): 115 return False 116 return (self.virtual == other.virtual and 117 self.actual == other.actual) 118 119 @property 120 def virtual(self): 121 return self.__virtual 122 123 @property 124 def actual(self): 125 return self.__actual 126 127 def register(self): 128 if self not in sys.meta_path: 129 sys.meta_path.append(self) 130 131 def find_module(self, fullname, path=None): 132 if fullname.startswith(self.virtual): 133 return self 134 return None 135 136 def load_module(self, fullname): 137 if fullname in sys.modules: 138 return sys.modules[fullname] 139 if not fullname.startswith(self.virtual): 140 raise ImportError(fullname) 141 realname = fullname.replace(self.virtual, self.actual) 142 try: 143 module = import_module(realname) 144 except ImportError: 145 message = 'Plugin "%s" is not installed. ' 146 message += 'Run "pip install %s" to install.' 147 message = message % (fullname, realname.replace('_', '-')) 148 raise ImportError(message) 149 sys.modules[realname] = module 150 sys.modules[fullname] = module 151 return module 152