def num_or_else(cand: Any) -> numbers.Number: asint = flow.silent(int)(cand) if decl.numeric(asint): return asint asfloat = flow.silent(float)(cand) if decl.numeric(asfloat): return asfloat return cand
def test_print_errors(): def error(): 1 / 0 f = print_errors(error) assert f.__name__ == 'error' assert 'ZeroDivisionError' in capture(silent(f)) g = print_errors(stack=False)(error) assert g.__name__ == 'error' assert capture(silent(g)).startswith('ZeroDivisionError')
def test_log_errors(): log = [] @log_errors(log.append) def f(x): return 1 / x silent(f)(1) silent(f)(0) assert len(log) == 1 assert log[0].startswith('Traceback') assert re.search(r'ZeroDivisionError: .*\n raised in f\(0\)$', log[0])
def test_log_calls_raise(): log = [] @log_calls(log.append, stack=False) def f(): raise Exception('something bad') silent(f)() assert log == [ "Call f()", "-> Exception: something bad raised in f()", ]
def take(self, limit=5): """ Take up to n (n = limit) posts/comments at a time. You can call this method as many times as you want. Once there are no more posts to take, it will return []. Returns: List of posts/comments in a batch of size up to `limit`. """ # get main posts only comment_filter = is_comment if self.comments_only else complement( is_comment) hist = filter(comment_filter, self.history) # filter out reblogs def match_author(x): return x['author'] == self.account.name hist2 = filter(match_author, hist) # post edits will re-appear in history # we should therefore filter out already seen posts def ensure_unique(post): if post['permlink'] not in self.seen_items: self.seen_items.add(post['permlink']) return True unique = filter(ensure_unique, hist2) serialized = filter(bool, map(silent(Post), unique)) batch = take(limit, serialized) return batch
def steem_btc_ticker(): prices = {} urls = [ "https://poloniex.com/public?command=returnTicker", "https://bittrex.com/api/v1.1/public/getmarketsummary?market=BTC-STEEM", ] responses = list(silent(requests.get)(u, timeout=30) for u in urls) for r in [ x for x in responses if hasattr(x, "status_code") and x.status_code == 200 and x.json() ]: if "poloniex" in r.url: data = r.json()["BTC_STEEM"] prices['poloniex'] = { 'price': float(data['last']), 'volume': float(data['baseVolume']) } elif "bittrex" in r.url: data = r.json()["result"][0] price = (data['Bid'] + data['Ask']) / 2 prices['bittrex'] = { 'price': price, 'volume': data['BaseVolume'] } if len(prices) == 0: raise RuntimeError( "Obtaining STEEM/BTC prices has failed from all sources.") return Tickers._wva([x['price'] for x in prices.values()], [x['volume'] for x in prices.values()])
def get_multi(urls): """ A generator of successful url fetching responses. """ with rs.Session() as s: for url in urls: resp = silent(get_safe)(url, session=s) if resp: yield resp
def sbd_btc_ticker(verbose=False): prices = {} urls = [ "https://poloniex.com/public?command=returnTicker", "https://bittrex.com/api/v1.1/public/getticker?market=BTC-SBD", ] responses = list(silent(requests.get)(u, timeout=5) for u in urls) for r in [ x for x in responses if hasattr(x, "status_code") and x.status_code == 200 and x.json() ]: if "poloniex" in r.url: data = r.json()["BTC_SBD"] if verbose: print("Spread on Poloniex is %.2f%%" % Tickers.calc_spread( data['highestBid'], data['lowestAsk'])) prices['poloniex'] = { 'price': float(data['last']), 'volume': float(data['baseVolume']) } elif "bittrex" in r.url: data = r.json()["result"] if verbose: print("Spread on Bittfex is %.2f%%" % Tickers.calc_spread(data['Bid'] + data['Ask'])) price = (data['Bid'] + data['Ask']) / 2 prices['bittrex'] = {'price': price, 'volume': 0} if len(prices) == 0: raise RuntimeError( "Obtaining SBD/BTC prices has failed from all sources.") return mean([x['price'] for x in prices.values()])
def btc_usd_ticker(verbose=False): prices = {} urls = [ "https://api.bitfinex.com/v1/pubticker/BTCUSD", # "https://api.coinbase.com/v2/prices/BTC-USD/spot", "https://api.kraken.com/0/public/Ticker?pair=XBTUSD", "https://www.okcoin.com/api/v1/ticker.do?symbol=btc_usd", "https://www.bitstamp.net/api/v2/ticker/btcusd/", "https://btc-e.com/api/2/btc_usd/ticker", ] responses = list(silent(requests.get)(u, timeout=5) for u in urls) for r in [ x for x in responses if hasattr(x, "status_code") and x.status_code == 200 and x.json() ]: if "bitfinex" in r.url: data = r.json() prices['bitfinex'] = { 'price': float(data['last_price']), 'volume': float(data['volume']) } elif "kraken" in r.url: data = r.json()['result']['XXBTZUSD']['p'] prices['kraken'] = { 'price': float(data[0]), 'volume': float(data[1]) } elif "okcoin" in r.url: data = r.json()["ticker"] prices['okcoin'] = { 'price': float(data['last']), 'volume': float(data['vol']) } elif "bitstamp" in r.url: data = r.json() prices['bitstamp'] = { 'price': float(data['last']), 'volume': float(data['volume']) } elif "btce" in r.url: data = r.json()["ticker"] prices['btce'] = { 'price': float(data['avg']), 'volume': float(data['vol_cur']) } if verbose: pprint(prices) if len(prices) == 0: raise RuntimeError( "Obtaining BTC/USD prices has failed from all sources.") # vwap return Tickers._wva([x['price'] for x in prices.values()], [x['volume'] for x in prices.values()])
def refresh(self): post_author, post_permlink = resolve_identifier(self.identifier) post = self.steemd.get_content(post_author, post_permlink) if not post["permlink"]: raise PostDoesNotExist("Post does not exist: %s" % self.identifier) # If this 'post' comes from an operation, it might carry a patch if "body" in post and re.match("^@@", post["body"]): self.patched = True # Parse Times parse_times = ["active", "cashout_time", "created", "last_payout", "last_update", "max_cashout_time"] for p in parse_times: post[p] = parse_time(post.get(p, "1970-01-01T00:00:00")) # Parse Amounts sbd_amounts = [ "total_payout_value", "max_accepted_payout", "pending_payout_value", "curator_payout_value", "total_pending_payout_value", "promoted", ] for p in sbd_amounts: post[p] = Amount(post.get(p, "0.000 SBD")) # turn json_metadata into python dict meta_str = post.get("json_metadata", "{}") post['json_metadata'] = silent(json.loads)(meta_str) or {} post["tags"] = [] post['community'] = '' if isinstance(post['json_metadata'], dict): if post["depth"] == 0: post["tags"] = ( post["parent_permlink"], *get_in(post, ['json_metadata', 'tags'], default=[]) ) post['community'] = get_in(post, ['json_metadata', 'community'], default='') # If this post is a comment, retrieve the root comment self.root_identifier, self.category = self._get_root_identifier(post) self._store_post(post)
def handle_post(post): meta = silent(json.loads)(post['json_metadata']) or {} if not isinstance(meta, dict): return meta.setdefault('tags', post['parent_permlink']) if APP_TAG in meta['tags']: post = Post(post).export() if post['depth'] > 0: return del post['id'] post['tags'] = list(post['tags']) post['json_metadata'] = fix_legacy_json(meta) PostModel.objects(url=post['url']).update(upsert=True, **post) print('saved', post['url'])
def get_replies(self): """ Return **first-level** comments of the post. """ post_author, post_permlink = resolve_identifier(self.identifier) replies = self.dpayd.get_content_replies(post_author, post_permlink) return map(silent(Post), replies)
def post(self, title, body, author, permlink=None, reply_identifier=None, json_metadata=None, comment_options=None, community=None, tags=None, beneficiaries=None, self_vote=False): """ Create a new post. If this post is intended as a reply/comment, `reply_identifier` needs to be set with the identifier of the parent post/comment (eg. `author/permlink`). Optionally you can also set json_metadata, comment_options and upvote the newly created post as an author. Setting category, tags or community will override the values provided in json_metadata and/or comment_options where appropriate. Args: title (str): Title of the post body (str): Body of the post/comment author (str): Account are you posting from permlink (str): Manually set the permlink (defaults to None). If left empty, it will be derived from title automatically. reply_identifier (str): Identifier of the parent post/comment (only if this post is a reply/comment). json_metadata (str, dict): JSON meta object that can be attached to the post. comment_options (str, dict): JSON options object that can be attached to the post. Example:: comment_options = { 'max_accepted_payout': '1000000.000 SMOKE', 'allow_votes': True, 'allow_curation_rewards': True, 'extensions': [[0, { 'beneficiaries': [ {'account': 'account1', 'weight': 5000}, {'account': 'account2', 'weight': 5000}, ]} ]] } community (str): (Optional) Name of the community we are posting into. This will also override the community specified in `json_metadata`. tags (str, list): (Optional) A list of tags (5 max) to go with the post. This will also override the tags specified in `json_metadata`. The first tag will be used as a 'category'. If provided as a string, it should be space separated. beneficiaries (list of dicts): (Optional) A list of beneficiaries for posting reward distribution. This argument overrides beneficiaries as specified in `comment_options`. For example, if we would like to split rewards between account1 and account2:: beneficiaries = [ {'account': 'account1', 'weight': 5000}, {'account': 'account2', 'weight': 5000} ] self_vote (bool): (Optional) Upvote the post as author, right after posting. """ # prepare json_metadata json_metadata = json_metadata or {} if isinstance(json_metadata, str): json_metadata = silent(json.loads)(json_metadata) or {} # override the community if community: json_metadata.update({'community': community}) # deal with the category and tags if isinstance(tags, str): tags = list(set(filter(None, (re.split("[\W_]", tags))))) category = None tags = tags or json_metadata.get('tags', []) if tags: if len(tags) > 5: raise ValueError('Can only specify up to 5 tags per post.') # first tag should be a category category = tags[0] json_metadata.update({"tags": tags}) # can't provide a category while replying to a post if reply_identifier and category: category = None # deal with replies/categories if reply_identifier: parent_author, parent_permlink = resolve_identifier( reply_identifier) if not permlink: permlink = derive_permlink(title, parent_permlink) elif category: parent_permlink = derive_permlink(category) parent_author = "" if not permlink: permlink = derive_permlink(title) else: parent_author = "" parent_permlink = "" if not permlink: permlink = derive_permlink(title) post_op = operations.Comment( **{ "parent_author": parent_author, "parent_permlink": parent_permlink, "author": author, "permlink": permlink, "title": title, "body": body, "json_metadata": json_metadata }) ops = [post_op] # if comment_options are used, add a new op to the transaction if comment_options or beneficiaries: options = keep_in_dict(comment_options or {}, [ 'max_accepted_payout', 'percent_steem_dollars', 'allow_votes', 'allow_curation_rewards', 'extensions' ]) # override beneficiaries extension if beneficiaries: # validate schema # or just simply vo.Schema([{'account': str, 'weight': int}]) schema = vo.Schema([{ vo.Required('account'): vo.All(str, vo.Length(max=16)), vo.Required('weight', default=10000): vo.All(int, vo.Range(min=1, max=10000)) }]) schema(beneficiaries) options['beneficiaries'] = beneficiaries default_max_payout = "1000000.000 SMOKE" comment_op = operations.CommentOptions( **{ "author": author, "permlink": permlink, "max_accepted_payout": options.get("max_accepted_payout", default_max_payout), "percent_steem_dollars": int(options.get("percent_steem_dollars", 10000)), "allow_votes": options.get("allow_votes", True), "allow_curation_rewards": options.get("allow_curation_rewards", True), "extensions": options.get("extensions", []), "beneficiaries": options.get("beneficiaries"), }) ops.append(comment_op) if self_vote: vote_op = operations.Vote( **{ 'voter': author, 'author': author, 'permlink': permlink, 'weight': 10000, }) ops.append(vote_op) return self.finalizeOp(ops, author, "posting")