def test_json_dumps_nested(self): """Test serializing a :class:`.DictObject` with a nested :class:`.DictObject` into JSON""" x = DictObject(hello='world', example='testing') x.layer = DictObject(test=True) j = json.dumps(x) self.assertEqual( j, '{"hello": "world", "example": "testing", "layer": {"test": true}}' )
def test_convert_to_dict(self): """Test converting a :class:`.DictObject` into a :class:`dict`""" x = DictObject(hello='world', example='testing') y = dict(x) self.assertEqual(x, y) self.assertEqual(y['hello'], 'world') self.assertEqual(y['example'], 'testing')
def add_defaults(parser: ArgumentParser, remove_defaults: Union[List[str], Set[str], tuple] = None, **overrides): """ Add the defaults from :func:`.get_arg_defaults` to the argument parser ``parser``. New defaults can be added, and existing defaults from :func:`.get_arg_defaults` can be overrided by specifying new/override defaults as kwargs. If you need to prevent certain defaults from :func:`.get_arg_defaults` or the passed ``overrides` kwargs from being set on ``parser``, pass their keys as a :class:`list` / :class:`set` / :class:`tuple` via the ``remove_defaults`` argument. Below is an example - create an arg parser, add ``--plugins`` to ``parser``, then add the common defaults to it. Remove the default for ``nodefile``, and override the default ``skip_apis`` to contain ``'bridge.get_trending_topics'``. Example:: >>> parser = ArgumentParser(description="my arg parser") >>> add_plugins(parser) >>> add_defaults(parser, remove_defaults=['nodefile'], skip_apis='bridge.get_trending_topics') :param ArgumentParser parser: A :class:`.ArgumentParser` instance :param List[str]|Set[str]|tuple remove_defaults: An optional list, set or tuple containing keys to remove from the defaults before adding them to ``parser`` :param overrides: Keyword arguments containing any additional defaults to add, or pre-existing defaults from :func:`.get_arg_default` to override (replace). :return DictObject defs: A :class:`.DictObject` containing the defaults which were set on ``parser`` """ defs = DictObject({**get_arg_defaults(), **overrides}) if not empty(remove_defaults, itr=True): for k in defs.keys(): if k not in remove_defaults: continue del defs[k] parser.set_defaults(**defs) return defs
def _zip_cols(cursor: Union[sqlite3.Cursor, GenericCursor, GenericAsyncCursor], row: iter): # combine the column names with the row data # so it can be used like a dict col_names = list(map(lambda x: x[0], cursor.description)) res = DictObject(zip(col_names, row)) return res
def scan_ip(self, ip: str) -> DictObject: dt = DictObject(country="unknown", city="unknown", asn="unknown", rdns='N/A') dt.rdns = self.get_rdns(ip) if not settings.USE_GEOIP: dt.asn = dt.country = dt.city = 'NO_GEOIP_DB', 'NO_GEOIP_DB', 'NO_GEOIP_DB' return dt dt.asn = self.get_asn(ip) if not empty(dt.asn): self._bump_asn(dt.asn) else: dt.asn = "unknown" dt.country, dt.city = self.get_location(ip) if not empty(dt.country): self._bump_country(dt.country) else: dt.country = "unknown" if not empty(dt.city): self._bump_city(dt.city) else: dt.city = "unknown" return dt
def get_arg_defaults() -> DictObject: """ Generates a :class:`.DictObject` containing sane argument defaults for use with a :class:`.ArgumentParser` instance. """ return DictObject( skip_apis='', plugins=settings.plugins, nodefile=settings.node_file, verbose=settings.verbose, quiet=settings.quiet, params='[]' )
def test_set_attr(self): """Test setting a dictionary key via an attribute ``x.y = 123``""" x = DictObject() x.testing = 'testattr' x.other_test = 'example' self.assertEqual(x['testing'], 'testattr') self.assertEqual(x['other_test'], 'example') self.assertEqual(x.testing, 'testattr') self.assertEqual(x.other_test, 'example')
def __init__(self, *args, **kwargs): self.buffer = [] self.hist_file = os.path.expanduser(f'~/{self.hist_file_name}') self.should_exit = False self.keywords = list(self.pyre.control_handlers.keys()) + list( self.pyre.rp.rule_handlers.keys()) self.completer = WordCompleter(self.keywords) open(self.hist_file, 'a').close() self.session = PromptSession(history=FileHistory(self.hist_file)) self._rendered_rules = DictObject(v4=[], v6=[], pyre=[]) self.rendered_at = len(self.buffer)
async def test_all( self, whitelist: List[str] = None, blacklist: List[str] = None) -> Union[DictObject, Dict[str, dict]]: """ Tests all supported RPC methods by :class:`.MethodTests` against :attr:`.host`, optionally specifying either a ``whitelist`` or ``blacklist`` This method returns a :class:`.DictObject` containing three keys:: - ``methods`` - A :class:`.dict` of JSONRPC string methods mapped to booleans, with ``True`` meaning the method was tested without any problems, while ``False`` means that an error occurred while testing this method. - ``errors`` - A :class:`.dict` which contains JSONRPC string methods mapped to the :class:`.Exception` which they raised. Only methods which raised an exception (i.e. those marked ``False`` in the ``methods`` dict) will be present here. - ``results`` - A :class:`.dict` which contains JSONRPC string methods mapped to the result their class testing method returned. This is usually a ``Tuple[Union[list,dict], float, int]``, which contains ``(response, time_taken_sec, tries)`` :param List[str] whitelist: A list of JSONRPC methods to exclusively test, e.g. ``['account_history_api.get_account_history', 'condenser_api.get_accounts']`` :param List[str] blacklist: A list of JSONRPC methods to skip testing, e.g. ``['bridge.get_trending_topics']`` :return Union[DictObject,Dict[str, dict]] res: A :class:`.DictObject` containing three :class:`.dict`'s: ``methods``, ``errors``, and ``results``. Full explanation of returned object in main pydoc body for this method :meth:`.test_all` """ res = DictObject(methods={}, errors={}, results={}) tasks = [] for meth, func in METHOD_MAP.items(): if whitelist is not None and meth not in whitelist: log.debug( "Skipping RPC method %s against host %s as method is not present in whitelist.", meth, self.host) continue # Skip APIs listed in SKIP_API_LIST only if we're not using a whitelist. If a whitelist was passed, then we should follow # the whitelist, and ignore the SKIP_API_list. if empty(whitelist) and meth in settings.SKIP_API_LIST: log.debug( "Skipping RPC method %s against host %s as method is in SKIP_API_LIST + no whitelist used.", meth, self.host) continue if blacklist is not None and meth in blacklist: log.debug( "Skipping RPC method %s against host %s as method is present in blacklist.", meth, self.host) continue tasks.append(self.loop.create_task(self._test_meth(func, meth))) for t in tasks: status, result, error, meth = await t res.methods[meth] = status if result is not None: res.results[meth] = result if error is not None: res.errors[meth] = error return res
def _node_to_row(node: Node): """ Convert a :class:`.Node` object into a dictionary which is safe for passing to an insert row function. :param Node node: The :class:`.Node` to convert :return DictObject node_dict: The node, converted into a dictionary. """ d = DictObject(url=node.url, network=node.network, enabled=node.enabled, fail_count=node.fail_count, last_fail=node.last_fail) if node.id is not None: d['id'] = node.id return d
def _neighbor_row(neighbor: Neighbor): n = DictObject( address=neighbor.address, domain=neighbor.domain, alias=empty_if(neighbor.alias, 'N/A'), connectionType=neighbor.connectionType, autopeeringId=empty_if(neighbor.autopeeringId, 'N/A'), numberOfAllTransactions=str(neighbor.numberOfAllTransactions), numberOfReceivedMilestoneReq=str( neighbor.numberOfReceivedMilestoneReq), numberOfSentHeartbeats=str(neighbor.numberOfSentHeartbeats), ) n.connected = f"{Fore.GREEN}YES{Fore.RESET}" if neighbor.connected else f"{Fore.RED}NO{Fore.RESET}" return NeighborTableRow(**n)
def _safe_geo( d: Union[GeoIPResult, Dict[str, Any]], force_safe=False, str_network=True, trim_geoasn=True, trim_geocity=True, **kwargs ) -> Union[dict, DictObject]: if force_safe or not isinstance(d, (DictObject, dict)): d = _safe_dict(d, **kwargs) d = DictObject(d) if str_network and not isinstance(d.network, str): d.network = str(d.network) if trim_geoasn and 'geoasn_data' in d: del d['geoasn_data'] if trim_geocity and 'geocity_data' in d: del d['geocity_data'] return d
def geo_view(ip: Union[str, IPv4Address, IPv4Address], ua: str = None, **extra) -> GeoResult: # data = DictObject(geo=DictObject(), hostname='', messages=[], ip_valid=False) # data = DictObject({**data, **extra}) data = GeoResult(ip=None, ua=ua, **extra) # data.geo = DictObject() try: data.init_ip(ip) data.ip_valid = True gdata = get_geodata(ip) if gdata is None: raise geoip2.errors.AddressNotFoundError(f"GeoIPResult was empty!") data.geo = DictObject(_safe_geo(get_geodata(ip))) data.geo.error = False except geoip2.errors.AddressNotFoundError: msg = f"IP address '{ip}' not found in GeoIP database." log.info(msg) data.geo = DictObject(error=True, message=msg) data.error = True data.messages += [msg] except ValueError: log.warning(f'The IP address "{ip}" was not valid... Use header: {cf["USE_IP_HEADER"]} / Header: "{cf["IP_HEADER"]}"') data.messages += ['Invalid IP address detected'] data.ip_valid = False return data
def _index(bformat=None): h = request.headers ip = get_ip() ua = h.get('User-Agent', 'Empty User Agent') q = merge_frm(req=request) data = DictObject(geo_view(ip, ua=ua)) wanted = wants_type() if 'format' in q else wants_type(fmt=bformat) if wanted == 'json': return jsonify(data) if wanted == 'text': fres = get_flat(ip, ua=ua, dtype=q.get('type', q.get('dtype', 'all'))) return Response(fres + "\n", status=200, content_type='text/plain') if wanted == 'yaml': return Response(dump_yaml(data), status=200, content_type='text/yaml') # return render_template('index.html', v4_host=cf['V4_HOST'], v6_host=cf['V6_HOST'], main_host=settings.MAIN_HOST, **data) return render_template('index.html', **data)
async def _test_methods(node: str, *methods: str, params='[]', auto_exit=True) -> Union[DictObject, Dict[str, dict]]: # mt = MethodTests(node) # loop = asyncio.get_event_loop() sup_methods = [meth for meth in methods if meth in get_supported_methods()] unsup_methods = [ meth for meth in methods if meth not in get_supported_methods() ] if len(unsup_methods) > 0: log.error( f"CAUTION: The RPC API method(s) '{unsup_methods}' are not supported yet. This means a proper, thorough, example " f"request + response test - has NOT been created for these method(s). Despite this, we'll try querying '{node}' " f"using the unsupported method(s) using the parameters '{params}', and checking for a non-empty 'result' key in the response." ) log.error( "You can use the CLI argument '--params' to change the parameters used for unsupported RPC methods." ) res = DictObject(methods={}, errors={}, results={}) if len(sup_methods) > 0: res = await MethodTests(node).test_all(whitelist=list(sup_methods)) if not empty(params): params = json.loads(params) else: params = [] for m in unsup_methods: ures = await _try_unknown_method(node=node, method=m, params=params, auto_exit=auto_exit) if ures is False: res.methods[m] = False res.errors[m] = "unknown error" continue res.methods[m] = True res.results[m] = ures[0] # return loop.run_until_complete(MethodTests(node).test_all(whitelist=list(methods))) return res
def __post_init__(self): kwargs = DictObject(self.raw_data) _maxs, _circ = self.max_supply, self.circulating_supply self.max_supply = conv_dec(kwargs.get('maxSupply', self.max_supply)) self.max_supply = conv_dec(kwargs.get('max_supply', self.max_supply)) self.circulating_supply = conv_dec( kwargs.get('circulatingSupply', self.circulating_supply)) self.circulating_supply = conv_dec( kwargs.get('circulating_supply', self.circulating_supply)) self.supply = Decimal(kwargs.get('supply', self.supply)) meta = self.metadata try: meta = {} if empty(meta, itr=True) else ( json.loads(meta) if isinstance(meta, str) else meta) except json.JSONDecodeError: log.warning("Failed to decode Token metadata as JSON: %s", meta) meta = {} self.metadata = TokenMetadata.from_dict(meta)
async def handle_error(err_code='UNKNOWN_ERROR', err_msg=None, code: int = None, exc: Exception = None, **kwargs): tpl = empty_if(kwargs.get('template'), 'api_error.html') res, extra = DictObject(error_code=err_code), {} if exc is not None: res.exc_name, res.exc_msg, res.traceback = str(type(exc)), str(exc), traceback.format_exc() log.warning("_handle_error exception type / msg: %s / %s", res.exc_name, res.exc_msg) log.warning("_handle_error traceback: %s", res.traceback) err: AppError = ERRORS.get(err_code, ERRORS['UNKNOWN_ERROR']) res.error_msg = err.message if err_msg is None else err_msg res.status_code = err.status if code is None else code if settings.DEBUG and exc is not None: extra: Dict[str, Any] = dict(exception=filter_form(res, 'traceback', 'exc_name', 'exc_msg')) if 'traceback' in extra['exception']: extra['exception']['traceback'] = res.traceback.split("\n") if await wants_json(): return error(res.error_code, msg=res.error_msg, code=res.status_code, extra=extra) tpl = await render_template( tpl, **res, data=json.dumps(error_dict(res.error_code, msg=res.error_msg, extra=extra), indent=4) ) return tpl, res.status_code
def add_known_asset_symbols(obj: Dict[str, Asset]) -> DictObject: """ For each :class:`.Asset` in ``obj``, make sure every asset type can be matched by both asset ID (i.e. IDs starting with "@@0000"), and their symbol (e.g. "HIVE"). :param Dict[str,Asset] obj: A :class:`.dict` or :class:`.DictObject` mapping asset IDs / symbols to :class:`.Asset` objects. :return DictObject new_assets: A new :class:`.DictObject` with both asset IDs (if applicable) and symbols mapped to :class:`.Asset`'s """ new_assets = DictObject(obj) # Iterable over each dict key + value, only handling keys which appear to be asset ID's starting with "@". # Create/update a key for the asset's symbol in new_assets to point to the asset_id's Asset object. for assetID, assetObject in obj.items(): # If this asset's ID doesn't start with an @, then we don't need to duplicate it into it's symbol. if assetID[0] != '@': continue # If the :class:`.Asset` object doesn't have an asset_id set, then set it to match the dictionary key assetObject.asset_id = assetID if empty( assetObject.asset_id) else assetObject.asset_id # Map the asset's symbol (e.g. HIVE) to the Asset object, so it can be matched by both asset ID and symbol. new_assets[assetObject.symbol] = assetObject return new_assets
def _safe_dict(d: Union[T, Dict[str, Any], list], safe_types: tuple = None, **kwargs) -> Union[T, list, dict, DictObject]: safe_types = tuple(empty_if(safe_types, (str, int, float), itr=True, zero=True)) iter_types = tuple(empty_if(kwargs.pop('iter_types', None), (dict, list, tuple, set), itr=True, zero=True)) unsafe_cast = kwargs.get('unsafe_cast', _try_dict) def _call_self(xdx): return _safe_dict(xdx, safe_types=safe_types, **kwargs) def _so(xdx): return _safe_obj(xdx, safe_types=safe_types, iter_types=iter_types, unsafe_cast=unsafe_cast, **kwargs) if isinstance(d, dict): log.debug(f"(_safe_dict) Object {d!r} appears to be a dict(-like) object - iterating over it, and recursively calling" f" _safe_obj.") safex = {k: _so(v) for k, v in d.items()} for k, v in safex.items(): if isinstance(v, dict) and not isinstance(d[k], dict): log.debug(f"(_safe_dict) Found new dict - converting it's contents to be safe by calling self (_safe_dict). " f"This new dict is (key: {k!r}): {v!r}") safex[k] = _call_self(v) # return DictObject({**safex, **unsafex, **objsafe}) return DictObject(safex) if isinstance(d, iter_types): itr_cast = type(d) log.debug(f"(_safe_dict) Object {d!r} appears to be a iterable object - iterating over it, and recursively calling" f" _safe_obj. iter_types: {iter_types!r}") return itr_cast([_so(v) for v in d]) log.debug(f"(_safe_dict) Object {d!r} doesn't match known types. Passing it to _safe_obj then calling self (_safe_dict)" f"with it's result") res_so = _so(d) log.debug(f"Result from _safe_obj: {res_so} (original object: {d!r})") res_self = _call_self(_so(d)) log.debug(f"Result from _safe_dict(_safe_obj(d)): {res_so} (original object: {d!r})") return res_self
class CustomAdmin(admin.AdminSite): """ To allow for custom admin views, we override AdminSite, so we can add custom URLs, among other things. """ custom_urls: List[PATH_TYPES] = [] custom_url_map: Dict[str, DictObject] = DictObject() _ct_admins = {} _sngl_lock = threading.Lock() def __init__(self, name='custom_admin'): # self.custom_urls = [] # self.custom_url_map = DictObject() super().__init__(name) @classmethod def admin_singleton(cls, singleton_name='default', *args, **kwargs): with cls._sngl_lock: n = singleton_name if empty(cls._ct_admins.get(n, None)): log.debug( "Creating new CustomAdmin singleton '%s'... args: %s | kwargs: %s", n, args, kwargs) cls._ct_admins[n] = cls(*args, **kwargs) return cls._ct_admins[n] def get_urls(self) -> List[PATH_TYPES]: """Returns a list of merged URLs by combining :meth:`.get_urls` via superclass with :attr:`.custom_urls`""" _urls = super(CustomAdmin, self).get_urls() urls = self.custom_urls return _urls + urls @property def custom_urls_reverse(self): """ Iterates over :attr:`.custom_url_map` and ensures all URL dictionaries have a ``url`` field, which points to their reversed URL based on their ``name`` """ for _, obj in self.custom_url_map.items(): if obj.hidden: continue if 'url' not in obj: obj['url'] = reverse(f"admin:{obj.name}") return self.custom_url_map # def each_context(self, request): # ctx = super().each_context(request) # ctx['custom_urls'] = self.custom_urls_reverse # return ctx def url_is_registered(self, url: str, fail=False) -> bool: """Returns ``True`` if the URL ``url`` exists within :attr:`.custom_urls` otherwise ``False``""" if url is None: return False for u in self.custom_urls: # noinspection PyProtectedMember if u.pattern._route == url: log.warning( "URL %s is already registered with CustomAdmin... Not registering!", url) if fail: raise FileExistsError( f"URL '{url}' is already registered with CustomAdmin!") return True return False @staticmethod def detect_human( obj: Union[callable, View, object, type]) -> Optional[str]: """ Extract a humanised name from an object:: >>> class ExampleView: ... pass >>> CustomAdmin.detect_human(ExampleView) 'Example View' >>> CustomAdmin.detect_human(ExampleView()) 'Example View' >>> def another_example(): ... pass >>> CustomAdmin.detect_human(another_example) 'Another Example' >>> class HasAName: ... pvx_human_name = 'This class has a name' >>> CustomAdmin.detect_human(HasAName) 'This class has a name' :param obj: The object to extract the human name from :return str|None name: The human name of the object, or ``None`` if failed to detect it. """ if hasattr(obj, 'pvx_human_name') and not empty(obj.pvx_human_name): return obj.pvx_human_name return human_name(obj) @staticmethod def detect_name(obj: Union[callable, View, object, type]) -> Optional[str]: """ Extract a snake-case name from a given object (class, instance, function or other type):: >>> class ExampleView: pass >>> CustomAdmin.detect_name(ExampleView) 'example_view' >>> CustomAdmin.detect_name(ExampleView()) 'example_view' >>> def anotherExample(): pass >>> CustomAdmin.detect_name(anotherExample) 'another_example' >>> class HasAName: ... pvx_name = 'hello_world' >>> CustomAdmin.detect_name(HasAName) 'hello_world' :param obj: The object to extract the snake-case name from :return str|None name: The snake-case name of the object, or ``None`` if failed to detect it. """ if hasattr(obj, 'pvx_name') and not empty(obj.pvx_name): return obj.pvx_name elif hasattr(obj, '__name__'): return camel_to_snake(obj.__name__) elif hasattr(obj, '__class__') and hasattr(obj.__class__, '__name__'): return camel_to_snake(obj.__class__.__name__) elif hasattr(obj, 'name') and not empty(obj.name): return obj.name log.warning( "No name specified by user for view, and cannot infer from view_obj.__name__ or other attributes... obj: %s", obj) return None @staticmethod def regex_has_params(url: str) -> bool: """Check if a URL contains view parameters""" if _RE_ANGLE_PARAMS.search(url) is not None: # log.debug("URL '%s' contains angle bracket (<str:example>) parameters", url) return True if _RE_BRACKET_PARAMS.search(url) is not None: # log.debug("URL '%s' contains round bracket ( (?P<x>()) ) parameters", url) return True return False def add_url(self, view_obj, url: URL_TYPES, human: str = None, hidden: bool = False, name: str = None, **kwargs) \ -> Optional[List[PATH_TYPES]]: """ Register a custom admin view with :class:`.CustomAdmin` **URL Types**: With :class:`.str` form, a single URL will be registered under ``/admin/`` with that URL. **In str form**:: >>> CustomAdmin().add_url(View(), 'someview/') With :class:`.list` form, simply specify a list of string URLs. Each URL will be given a name with a number appended to the end (based on URL position). For the below example, assuming this was a function view named ``my_view``, then the first URL would get the name ``my_view_1``, and the second ``my_view_2``. **In list form**:: >>> CustomAdmin().add_url(View(), ['myview/', 'myview/<int:id>/']) With :class:`.dict` form, URLs are specified as keys, mapped to a unique name for the URL. This allows you to specify arbitrary names for each URL, which will be stable (under the ``admin:`` prefix), allowing you to safely reference them from within templates, e.g. ``{% url 'admin:user_info_by_username' user.username %}`` **In dict form**:: >>> CustomAdmin().add_url(View(), { ... 'user_info/': 'user_info_index', ... 'user_info/<str:username>': 'user_info_by_username' ... }) :param callable|View view_obj: A Django view (function-based or class-based) to register a URL for :param str url: An individual URL as a string to register the view under :param List[str] url: With :class:`.list` form, simply specify a list of string URLs. Each URL will be given a name with a number appended to the end (based on URL position). For the below example, assuming this was a function view named ``my_view``, then the first URL would get the name ``my_view_1``, and the second ``my_view_2``. In list form: ``['myview/', 'myview/<int:id>/']`` :param dict url: With :class:`.dict` form, URLs are specified as keys, mapped to a unique name for the URL. This allows you to specify arbitrary names for each URL, which will be stable (under the ``admin:`` prefix), allowing you to safely reference them from within templates, e.g. ``{% url 'admin:user_info_by_username' user.username %}`` In dict form: ``{'user_info/': 'user_info_index', 'user_info/<str:username>': 'user_info_by_username'}`` :param str human: A human readable link name/title for the page - which would be shown in the admin panel :param bool hidden: (Default: ``False``) Whether or not to hide this URL from the admin page list :param str name: A unique URL name for referencing with Django URL name lookups :param kwargs: Additional options :keyword bool hide_extras: (Default: ``True``) When more than one URL is specified in ``url`` using a list/dict, if hide_extra is True, then only the first URL in the list/dict of URLs will use the user-specified ``hidden`` parameter. The rest of the URLs will have ``hidden=True`` :keyword bool hide_params: If hide_params is True, URLs which contain route parameters (e.g. ``<str:username>``) will be hidden by default, to prevent errors caused by trying to reverse their URL in the admin panel custom view list. :return List[PATH_TYPES] custom_urls: If successful, returns the current list of URLs from :attr:`.custom_urls` """ if empty(view_obj): log.error( f"view_obj is empty, cannot register! url: {url} | human: {human} | hidden: {hidden} | name: {name} | kwargs: {kwargs}" ) return None url = camel_to_snake(view_obj.__name__) + '/' if empty(url) else url # When more than one URL is specified in ``url`` using a list/dict, if hide_extra is True, then only the first URL # in the list/dict of URLs will use the user-specified ``hidden`` parameter. # The rest of the URLs will have hidden=True hide_extra = kwargs.get('hide_extra', True) # If hide_params is True, URLs which contain route parameters (e.g. ``<str:username>``) will be hidden by default, to prevent # errors caused by trying to reverse their URL in the admin panel custom view list. hide_params = kwargs.get('hide_params', True) name = name if not empty(name) else self.detect_name(view_obj) human = human if not empty(human) else self.detect_human( empty_if(view_obj, name)) #### # Handle URLs specified as a list #### if isinstance(url, list): url = [u for u in list(url) if not self.url_is_registered(u)] name_number = 1 orig_name = name orig_hidden = hidden for u in url: if name_number > 1: name = f"{orig_name}_{name_number}" if hide_extra: hidden = True if self.regex_has_params(u) and hide_params: hidden = True self.add_url(view_obj, url=u, human=human, hidden=hidden, name=name, **kwargs) name_number += 1 hidden = orig_hidden return self.custom_urls #### # Handle URLs specified as a dict of url:name #### elif isinstance(url, dict): # url = {u: n for u, n in dict(url).items() if not self.url_is_registered(u)} orig_hidden = hidden name_number = 1 for u, n in url.items(): if self.url_is_registered(u): continue if name_number > 1 and hide_extra: hidden = True if self.regex_has_params(u) and hide_params: hidden = True self.add_url(view_obj, url=u, human=human, hidden=hidden, name=n, **kwargs) name_number += 1 hidden = orig_hidden return self.custom_urls #### # Handle a plain string URL #### elif isinstance(url, str): if self.url_is_registered(url): return self.custom_urls #### # String URL handling continued #### if empty(name): # if hasattr(view_obj, '__name__'): # name = camel_to_snake(view_obj.__name__) # else: log.warning( "No name specified by user for view, and cannot infer from view_obj.__name__ ..." ) name = None # If a URL contains Django route parameters e.g. ``<str:example>``, it's best to hide them by default, otherwise # they'll cause issues when they're reversed in .custom_urls_reverse if self.regex_has_params(url) and hide_params: hidden = True # Class-based views need to be registered using .as_view() view_obj = view_obj.as_view() if isclass(view_obj) else view_obj self.custom_urls.append(path(url, view_obj, name=name)) self.custom_url_map[url] = DictObject( name=name, route=url, human=empty_if(human, human_name(empty_if(name, "unknown_custom_view"))), hidden=hidden) return self.custom_urls def wrap_register(self, view, model: Model = None, url: URL_TYPES = None, human: str = None, hidden: bool = False, name: str = None, **kwargs): """ This is a wrapper method which routes calls from :func:`.register_url` / :func:`.ct_register` to the correct method, depending on whether a Django :class:`.ModelAdmin` view is being registered, or whether a custom user function/class admin view is being registered. See :meth:`.add_url` for full docs. Most params are just passed through to :meth:`.add_url`. :param callable|type|Type[View]|View view: The function/class view to register, or a :class:`.ModelAdmin` :param Model model: Only applicable when registering :class:`.ModelAdmin`'s :param str|list|dict url: The URL or multiple URLs to register ``view`` under :param str human: The human friendly name for the view - displayed on the admin index list of custom views :param bool hidden: (Default: ``False``) Whether or not to hide this URL from the admin page list :param str name: A unique URL name for referencing with Django URL name lookups :param kwargs: :return: """ if url is None: if isclass(view) and issubclass(view, admin.ModelAdmin): if empty(model): raise ValueError( f"Wrapped ModelAdmin {view.__name__} but no model passed to ct_register..." ) # log.debug("Registering ModelAdmin view: %s", view.__name__) self.register(model, view) return raise ValueError( "Wrapped classes without url's must subclass admin.ModelAdmin." ) self.add_url(view_obj=view, url=url, human=human, hidden=hidden, name=name, **kwargs)
def add_url(self, view_obj, url: URL_TYPES, human: str = None, hidden: bool = False, name: str = None, **kwargs) \ -> Optional[List[PATH_TYPES]]: """ Register a custom admin view with :class:`.CustomAdmin` **URL Types**: With :class:`.str` form, a single URL will be registered under ``/admin/`` with that URL. **In str form**:: >>> CustomAdmin().add_url(View(), 'someview/') With :class:`.list` form, simply specify a list of string URLs. Each URL will be given a name with a number appended to the end (based on URL position). For the below example, assuming this was a function view named ``my_view``, then the first URL would get the name ``my_view_1``, and the second ``my_view_2``. **In list form**:: >>> CustomAdmin().add_url(View(), ['myview/', 'myview/<int:id>/']) With :class:`.dict` form, URLs are specified as keys, mapped to a unique name for the URL. This allows you to specify arbitrary names for each URL, which will be stable (under the ``admin:`` prefix), allowing you to safely reference them from within templates, e.g. ``{% url 'admin:user_info_by_username' user.username %}`` **In dict form**:: >>> CustomAdmin().add_url(View(), { ... 'user_info/': 'user_info_index', ... 'user_info/<str:username>': 'user_info_by_username' ... }) :param callable|View view_obj: A Django view (function-based or class-based) to register a URL for :param str url: An individual URL as a string to register the view under :param List[str] url: With :class:`.list` form, simply specify a list of string URLs. Each URL will be given a name with a number appended to the end (based on URL position). For the below example, assuming this was a function view named ``my_view``, then the first URL would get the name ``my_view_1``, and the second ``my_view_2``. In list form: ``['myview/', 'myview/<int:id>/']`` :param dict url: With :class:`.dict` form, URLs are specified as keys, mapped to a unique name for the URL. This allows you to specify arbitrary names for each URL, which will be stable (under the ``admin:`` prefix), allowing you to safely reference them from within templates, e.g. ``{% url 'admin:user_info_by_username' user.username %}`` In dict form: ``{'user_info/': 'user_info_index', 'user_info/<str:username>': 'user_info_by_username'}`` :param str human: A human readable link name/title for the page - which would be shown in the admin panel :param bool hidden: (Default: ``False``) Whether or not to hide this URL from the admin page list :param str name: A unique URL name for referencing with Django URL name lookups :param kwargs: Additional options :keyword bool hide_extras: (Default: ``True``) When more than one URL is specified in ``url`` using a list/dict, if hide_extra is True, then only the first URL in the list/dict of URLs will use the user-specified ``hidden`` parameter. The rest of the URLs will have ``hidden=True`` :keyword bool hide_params: If hide_params is True, URLs which contain route parameters (e.g. ``<str:username>``) will be hidden by default, to prevent errors caused by trying to reverse their URL in the admin panel custom view list. :return List[PATH_TYPES] custom_urls: If successful, returns the current list of URLs from :attr:`.custom_urls` """ if empty(view_obj): log.error( f"view_obj is empty, cannot register! url: {url} | human: {human} | hidden: {hidden} | name: {name} | kwargs: {kwargs}" ) return None url = camel_to_snake(view_obj.__name__) + '/' if empty(url) else url # When more than one URL is specified in ``url`` using a list/dict, if hide_extra is True, then only the first URL # in the list/dict of URLs will use the user-specified ``hidden`` parameter. # The rest of the URLs will have hidden=True hide_extra = kwargs.get('hide_extra', True) # If hide_params is True, URLs which contain route parameters (e.g. ``<str:username>``) will be hidden by default, to prevent # errors caused by trying to reverse their URL in the admin panel custom view list. hide_params = kwargs.get('hide_params', True) name = name if not empty(name) else self.detect_name(view_obj) human = human if not empty(human) else self.detect_human( empty_if(view_obj, name)) #### # Handle URLs specified as a list #### if isinstance(url, list): url = [u for u in list(url) if not self.url_is_registered(u)] name_number = 1 orig_name = name orig_hidden = hidden for u in url: if name_number > 1: name = f"{orig_name}_{name_number}" if hide_extra: hidden = True if self.regex_has_params(u) and hide_params: hidden = True self.add_url(view_obj, url=u, human=human, hidden=hidden, name=name, **kwargs) name_number += 1 hidden = orig_hidden return self.custom_urls #### # Handle URLs specified as a dict of url:name #### elif isinstance(url, dict): # url = {u: n for u, n in dict(url).items() if not self.url_is_registered(u)} orig_hidden = hidden name_number = 1 for u, n in url.items(): if self.url_is_registered(u): continue if name_number > 1 and hide_extra: hidden = True if self.regex_has_params(u) and hide_params: hidden = True self.add_url(view_obj, url=u, human=human, hidden=hidden, name=n, **kwargs) name_number += 1 hidden = orig_hidden return self.custom_urls #### # Handle a plain string URL #### elif isinstance(url, str): if self.url_is_registered(url): return self.custom_urls #### # String URL handling continued #### if empty(name): # if hasattr(view_obj, '__name__'): # name = camel_to_snake(view_obj.__name__) # else: log.warning( "No name specified by user for view, and cannot infer from view_obj.__name__ ..." ) name = None # If a URL contains Django route parameters e.g. ``<str:example>``, it's best to hide them by default, otherwise # they'll cause issues when they're reversed in .custom_urls_reverse if self.regex_has_params(url) and hide_params: hidden = True # Class-based views need to be registered using .as_view() view_obj = view_obj.as_view() if isclass(view_obj) else view_obj self.custom_urls.append(path(url, view_obj, name=name)) self.custom_url_map[url] = DictObject( name=name, route=url, human=empty_if(human, human_name(empty_if(name, "unknown_custom_view"))), hidden=hidden) return self.custom_urls
import copy import re import threading from inspect import isclass from typing import List, Optional, Union, Dict from django.contrib import admin from django.db.models import Model from django.http import HttpRequest from django.urls import URLResolver, URLPattern, path, reverse from django.views import View from privex.helpers import camel_to_snake, empty, human_name, empty_if, DictObject import logging log = logging.getLogger(__name__) STORE = DictObject(is_setup=False) _RE_ANGLE_PARAMS = re.compile(r'<[a-zA-Z0-9_:-]+>') _RE_BRACKET_PARAMS = re.compile(r'\(\?P.*\)') URL_TYPES = Optional[Union[str, List[str], Dict[str, str]]] PATH_TYPES = Union[URLResolver, URLPattern] class CustomAdmin(admin.AdminSite): """ To allow for custom admin views, we override AdminSite, so we can add custom URLs, among other things. """ custom_urls: List[PATH_TYPES] = [] custom_url_map: Dict[str, DictObject] = DictObject()
CHAIN_ASSETS = DictObject(HIVE=DictObject({ '@@000000013': Asset(symbol='HBD', precision=3, asset_id='@@000000013'), '@@000000021': Asset(symbol='HIVE', precision=3, asset_id='@@000000021'), '@@000000037': Asset(symbol='VESTS', precision=6, asset_id='@@000000037'), }), BLURT=DictObject({ '@@000000021': Asset(symbol='BLURT', precision=3, asset_id='@@000000021'), '@@000000037': Asset(symbol='VESTS', precision=6, asset_id='@@000000037'), }), STEEM=DictObject({ '@@000000013': Asset(symbol='SBD', precision=3, asset_id='@@000000013'), '@@000000021': Asset(symbol='STEEM', precision=3, asset_id='@@000000021'), '@@000000037': Asset(symbol='VESTS', precision=6, asset_id='@@000000037'), }), GOLOS=DictObject({ 'GOLOS': Asset(symbol='GOLOS', precision=3, network=CHAIN.GOLOS.value), 'GBG': Asset(symbol='GBG', precision=3, network=CHAIN.GOLOS.value), 'GESTS': Asset(symbol='GESTS', precision=6, network=CHAIN.GOLOS.value), }))
NeighborTableRow = namedtuple( 'NodeTableRow', 'address connected domain alias connectionType autopeeringId numberOfAllTransactions ' 'numberOfReceivedMilestoneReq numberOfSentHeartbeats', ) NeighborTableColumn = namedtuple('NeighborTableColumn', 'title title_padding content_padding', defaults=(25, 25)) # Mapping of NeighborTableRow key, to a tuple of (column_title, title_padding, content_padding) table_columns: Dict[str, NeighborTableColumn] = DictObject( address=NeighborTableColumn('Address', 55, 55), connected=NeighborTableColumn('Connected?', 22, 32), domain=NeighborTableColumn('Domain', 40, 40), alias=NeighborTableColumn('Alias', 40, 40), connectionType=NeighborTableColumn('Proto', 20, 20), autopeeringId=NeighborTableColumn('Peer ID', 35, 35), numberOfAllTransactions=NeighborTableColumn('Total TX', 25, 25), numberOfReceivedMilestoneReq=NeighborTableColumn('Recv Milestones', 30, 30), numberOfSentHeartbeats=NeighborTableColumn('Heartbeats', 25, 25), ) def _neighbor_row(neighbor: Neighbor): n = DictObject( address=neighbor.address, domain=neighbor.domain, alias=empty_if(neighbor.alias, 'N/A'), connectionType=neighbor.connectionType, autopeeringId=empty_if(neighbor.autopeeringId, 'N/A'), numberOfAllTransactions=str(neighbor.numberOfAllTransactions),
from privex.helpers import DictObject from privex.steemengine import SteemEngineToken, SteemEngineHistory __all__ = [ 'st', 'SEBase', 'SteemBaseTests', 'HiveBaseTests', 'NetworkBaseTests', 'FAKE_TOKEN', 'REAL_TOKEN', 'ACTIVE_TOKENS', 'TEST_ACC', 'FAKE_ACC' ] # SteemEngineToken.custom_beem(node=['https://api.steemit.com']) st = SteemEngineToken() FAKE_TOKEN = 'NONEXISTANTTOKEN' REAL_TOKEN = 'SGTK' ACTIVE_TOKENS = DictObject(steem='ENG', hive='BEE') TEST_ACC = 'someguy123' FAKE_ACC = 'non-existent-account999' class SEBase(unittest.TestCase): s_nodes = ['https://api.steemit.com'] s_kwargs = dict(network='steem') @classmethod def setUpClass(cls) -> None: cls.stoken = SteemEngineToken() class SteemBaseTests(unittest.TestCase): @classmethod def setUpClass(cls) -> None:
from decimal import Decimal import pytest from privex.helpers import DictObject, empty from privex.eos.lib import Api, EOSBlock, Node from privex.eos.node import NodeManager, DEFAULT_ADAPTER from privex.eos.objects import EOSAccount EXAMPLE_BLOCK_NUM = 94000000 EXAMPLE_BLOCK_DATA = DictObject( timestamp='2019-12-08T23:19:55.000', producer='eoshuobipool', block_num=94000000, ref_block_prefix=3607919330, previous='059a537fa917845911fc05e8f6b305e90a837eaba935ae65617125291cb307b4', id='059a5380852aef1ee27a0cd75953f76bb334ad402b4e0360dada1a17ee486357', producer_signature= 'SIG_K1_K9EoSbEcLaeXgJWwwQTiL9k4RsQW2DwS7nng77v6y8nZ8gJYC6qBknumsnq' 'Q3vXChsK2KpkgzKVcPEC8YgT8RUxWrHJatP', _total_txs=13) RANGE_START = EXAMPLE_BLOCK_NUM RANGE_END = RANGE_START + 20 @pytest.fixture() async def eos_api(): Api().node_manager.adapter.recreate_schemas() eos = Api() yield eos
def as_dict(cls) -> Union[DictObject, Dict[str, str]]: """Return this enum as a :class:`.DictObject` - chain names mapped to their string chain ID""" # noinspection PyTypeChecker return DictObject({chain.name: chain.value for chain in list(cls)})
async def _q_ticker(self, from_coin: str, to_coin: str) -> DictObject: from_coin, to_coin = from_coin.upper(), to_coin.upper() res = await self._query(from_coin, to_coin, endpoint='/ticker') return DictObject(last=res['lastTradeRate'], bid=res['bidRate'], ask=res['askRate'])
async def _q_summary(self, from_coin: str, to_coin: str) -> DictObject: from_coin, to_coin = from_coin.upper(), to_coin.upper() res = await self._query(from_coin, to_coin, endpoint='/summary') return DictObject(high=res['high'], low=res['low'], volume=res['volume'])
except Exception: if try_class: cld = d.__class__ if isinstance(d, object) else d if cld.__name__ not in dir(__builtins__): try: return _class_dict(d) except Exception: pass return fb_cast(d) CONVERTERS = { bytes: stringify, GeoIPResult: DictObject, GeoData: DictObject, geoip2.models.ASN: lambda a: DictObject(a.raw), geoip2.models.City: lambda a: DictObject(a.raw), geoip2.models.Country: lambda a: DictObject(a.raw), GeoType: lambda a: a.value, IPv4Address: str, IPv6Address: str, IPv4Network: str, IPv6Network: str, } def _do_convert(d, fallback=None, fail=False): for k, v in CONVERTERS.items(): if not isinstance(d, k) and not type(d) == k: continue try: log.debug(f"Found converter for {d!r} - calling converter function (key: {k!r}): {v!r} ") res = v(d) log.debug(f"Result after passing {d!r} to converter is: {res!r}") return res