def slugify(value, substitutions=()): ''' Normalizes string, converts to lowercase, removes non-alpha characters, and converts spaces to hyphens. Took from Django sources. ''' # TODO Maybe steal again from current Django 1.5dev value = Markup(value).striptags() # value must be unicode per se import unicodedata from unidecode import unidecode # unidecode returns str in Py2 and 3, so in Py2 we have to make # it unicode again value = unidecode(value) if isinstance(value, six.binary_type): value = value.decode('ascii') # still unicode value = unicodedata.normalize('NFKD', value).lower() for src, dst in substitutions: value = value.replace(src.lower(), dst.lower()) value = re.sub('[^\w\s-]', '', value).strip() value = re.sub('[-\s]+', '-', value) # we want only ASCII chars value = value.encode('ascii', 'ignore') # but Pelican should generally use only unicode return value.decode('ascii')
def pretty(value): if isinstance(value, dict): page = markup.page() page.ul(_class='dict') for k, v in value.items(): if isinstance(v, list) and v: pretty_value = Markup.escape(', '.join(v)) elif isinstance(v, dict) and v: subul = markup.page() subul.ul(_class='subdict') for subk, subv in v.items(): subul.li() subul.span('%s: ' % subk) subul.span(Markup.escape(subv)) subul.li.close() subul.ul.close() pretty_value = subul() elif v: pretty_value = Markup.escape(v) else: continue page.li() page.span('%s:' % k.capitalize().replace('_', ' ')) page.span(pretty_value) page.li.close() page.ul.close() return page() elif isinstance(value, list): return Markup.escape(', '.join(value)) else: page = markup.page() page.span(Markup.escape(value), _class='simple') return page()
def lists_edit(name=None): if 'username' not in session: return goto_login(fname(), fparms()) action = 'delete' if request.args.get('action', request.form.get('action')) == 'delete' else 'add' item = request.args.get('item', request.form.get('item')) if not name or not action or not item: return redirect(url_for('lists')) listname = getattr(userlists, name).printedname if request.method == 'POST': if name == 'whitelist': restartstring = '<span class="halflink" onclick="document.getElementById(\'restartform\').submit();">Restart</span> the server to apply your changes.' else: restartstring = '' if action == 'add': if getattr(userlists, name).add(item): flash('<i>%s</i> added to %s. %s' % (Markup.escape(item), listname, restartstring), 'success') else: flash('<i>%s</i> is already in %s.' % (Markup.escape(item), listname), 'info') elif action == 'delete': if getattr(userlists, name).remove(item): flash('<i>%s</i> deleted from %s. %s' % (Markup.escape(item), listname, restartstring), 'success') else: flash('<i>%s</i> is not in %s.' % (Markup.escape(item), listname), 'info') returnpage = request.form.get('returnpage', 'lists') return redirect(url_for(returnpage, username=item)) return render_template('lists_delete.html', navigation=get_navi('lists'), name=name, action=action, item=item, listname=listname)
def new_announcement(): """Create new announcement.""" def respond(): response = dict(template='admin/new_announcement.html', title=gettext("Write a new post"), form=form) return handle_content_type(response) form = AnnouncementForm() del form.id # project_sanitized, owner_sanitized = sanitize_project_owner(project, owner, current_user) if request.method != 'POST': ensure_authorized_to('create', Announcement()) return respond() if not form.validate(): flash(gettext('Please correct the errors'), 'error') return respond() announcement = Announcement(title=form.title.data, body=form.body.data, published=form.published.data, media_url=form.media_url.data, user_id=current_user.id) ensure_authorized_to('create', announcement) announcement_repo.save(announcement) msg_1 = gettext('Annnouncement created!') markup = Markup('<i class="icon-ok"></i> {}') flash(markup.format(msg_1), 'success') return redirect_content_type(url_for('admin.announcement'))
def _format_attrs(attrs, escape_attrs=True): out = [] for name, value in sorted(attrs.items()): if escape_attrs: name = Markup.escape(name) value = Markup.escape(value) out.append(' {name}="{value}"'.format(name=name, value=value)) return ''.join(out)
def get_killmail_descriptions(): description = Markup(u"Acceptable Killmail Links:<ul>") desc_entry = Markup(u"<li>{}</li>") killmail_descs = [desc_entry.format(km.description) for km in\ current_app.killmail_sources] description += Markup(u"").join(killmail_descs) description += Markup(u"</ul>") return description
def extension_filename(filename,ext): ''' Añade la extension al nombre de archivo ''' fn = Markup(filename).striptags()[:512] #si la extension no viene en el nombre se añade if ext and not fn.lower().endswith("."+ext.lower()): fn = fn+"."+ext return fn
def generate_links(query_list, model=None, model_key=None): link = Markup("""<a href="{}">{}</a> - <a href="{}">Delete</a>""") model_key = [key.key for key in model_key.columns][0] for entry in query_list: key = getattr(entry, model_key, 1) name = model.__name__ edit_url = url_for('admin.edit', model_name=name, model_url_key=key) delete_url = url_for('admin.delete', model_name=name, model_url_key=key) yield link.format(edit_url, entry, delete_url)
def index(): li = Markup("""<li><a href="{}">{}</a></li>""") results = db.metadata.tables.keys() results.sort() results = [li.format(url_for("app.view_table", table_name=tbl_name), tbl_name) for tbl_name in results] results = Markup("<ul>") + Markup("\n").join(results) + Markup("</ul>") return render_template("layout.html", content=results)
def safe_non_valid_input_error(user_input,field_name): r""" Returns a formatted error message where all non-fixed parameters (in particular user input) is escaped. """ msg = Markup("Error: <span style='color:black'>")+Markup.escape(user_input) msg += Markup("</span>") msg += Markup(" is not a valid input for <span style='color:black'>") msg += Markup.escape(field_name)+Markup("</span>") return msg
def test_permissions_custom_word(): title = 'Testing custom permission, the word should be "yep"' data = { "app.access('test_permission_word')": app.access('test_permission_word'), "app.access('test_permission_word', word='qwer')": app.access('test_permission_word', word='qwer'), "app.access('test_permission_word', word='yep')": app.access('test_permission_word', word='yep') } data = Markup('<br/>').join(["%s: %s" % (k,v) for k,v in data.items()]) return render_template( 'test/index.html', title=title, data=data )
def delete_announcement(id): announcement = announcement_repo.get_by(id=id) if announcement is None: raise abort(404) ensure_authorized_to('delete', announcement) announcement_repo.delete(announcement) msg_1 = gettext('Announcement deleted!') markup = Markup('<i class="icon-ok"></i> {}') flash(markup.format(msg_1), 'success') return redirect_content_type(url_for('admin.announcement'))
def play(): logged = False user_log = '' if (session): if 'logged_in' not in session: return redirect(url_for('login')) else: if session['logged_in']: logged = True user_log = Markup('<a class="nav-link" href="/profile"> Hello, {0} </a>'.format(session['user_name'])) else: return redirect(url_for('login')) else: return redirect(url_for('login')) difficulty = request.args.get("diff") song_id = request.args.get("sg") if not song_id or not difficulty: # display error page pass song = get_song_details(song_id) word_list = get_words_to_hide(song_id, int(difficulty)) song_tags = get_tags_for_song(song_id) lyrics = song.lyrics.replace('\n', '<br>') html_to_replace = Markup('<div class="form-group"> \ <div class="input-group"> \ <input type="text" id="{0}" class="missing-word form-control" size="{1}" maxlength="{1}" data-word="{3}"> \ <span class="glyphicon glyphicon-eye-open input-group-addon" data-toggle="tooltip" data-placement="bottom" aria-hidden="true" title="{2}"> \ </span> \ </div> \ </div>') i = 0 for word in word_list: hint = get_hint_for_word(word, int(difficulty)) lyrics, k = re.subn(r'\s({0})\s'.format(word), html_to_replace.format("word-{0}".format(i), len(word) + 1, hint, word), lyrics, count=max([1, int(difficulty) - 1])) i += k m, s = divmod(int(song.length), 60) song_duration = "%02d:%02d" % (m, s) video_url = song.video_url[song.video_url.index('v=') + 2:] keep_ids, _ = get_user_keep(session['user_id']) keep_next = get_next_keep(session['user_id'], song_id) keep_count = get_keep_count_for_song(song_id) play_count = get_play_count_for_song(song_id) return render_template('play.html', logged_in=logged, profile_login=user_log, user_id=session['user_id'], song_id=song_id, lyrics=lyrics, song_tags=song_tags, song_artist=song.artist, song_title=song.title, release_year=song.release_year, song_duration=song_duration, video_url=video_url, num_of_words=i, difficulty=int(difficulty), keep_ids=keep_ids, keep_next=keep_next, keep_count=keep_count, play_count=play_count)
def add_new_language(): user = fetch_id_from_userid(current_user.id) bcp = request.args.get('bcp', None) bcp = str(Markup.escape(bcp)) iso = request.args.get('iso', None) iso = str(Markup.escape(iso)) name = request.args.get('name', None) name = str(Markup.escape(name)) if bcp and name: dbinsert = insert_new_language(bcp, iso, name, user) return jsonify(result=dbinsert) else: return jsonify(result=False)
def profile_edit(user_id=-1): """ изменение профиля пользователя """ user = current_user if session and session['logged_in']: # если юзер залогинен if user_id >= 0: if 'super_admin' in user.roles: # если запрошен user_id и если есть роль admin user = load_user(user_id) else: return redirect(url_for('users.profile')) # возвращаем форму изменения профайла if request.method == 'GET': return render_template("users/profile_edit.html", user=user) # все остальное это POST запрос, т.е. submit на форму изменения профиля # валидируем форму valid, error = profile_edit_validate(request.form, user) if not valid: return render_template("users/profile_edit.html", error=error, user=user) name = "" if 'name' in request.form: name = Markup.escape(request.form['name']) surname = "" if 'surname' in request.form: surname = Markup.escape(request.form['surname']) if 'new_password' in request.form: # если админ редактирует профиль и оставляет пароль пустым, # то не меняем этот пароль if 'super_admin' not in current_user.roles: user.set_password(request.form['new_password']) else: # если админ редактирует запись, и вбивает пользователю # новый пароль, то устанавливаем его if request.form['old_password'] == '': user.set_password(request.form['new_password']) # изменяем пользователя user['surname'] = surname user['name'] = name user.save() return redirect(request.args.get('next') or url_for('users.profile'))
def handle_starttag(self, tag, attrs): self.container.append("<%s>" % tag) attrs_string = '' if attrs: for attr in attrs: str = '%s="%s" ' % (attr[0], attr[1]) attrs_string = attrs_string + str if attrs_string and attrs_string[-1] == " ": attrs_string = attrs_string[:-1] tag_string = Markup.escape('<%s %s>' % (tag, attrs_string)) else: tag_string = Markup.escape('<%s>' % tag) self.div_wrap = self.div_wrap + Markup('<span class="slack-%s">' % tag) + \ tag_string + Markup('</span>')
def deleteUser(id): with app.app_context(): db = get_db() user = db.getUserByID(id) if user is not None: db.deleteUser(id) db.commit() flash('{0} {1} has been succesfully deleted.' .format(Markup.escape(user['name']), Markup.escape(user['surname'])), 'success') else: flash('Unable to delete user id {0}: User does not exist.'.format(id), 'error') return redirect(url_for('index'))
def td_contents(self, item, attr_list, btn_class=None): if btn_class: return '<form method="post" action="{url}">'\ '<button class="{btn_class}" type="submit">{text}</button>'\ '</form>'.format( url=self.url(item), text=Markup.escape(self.text(item, attr_list)), btn_class=btn_class) else: return '<form method="post" action="{url}">'\ '<button type="submit">{text}</button>'\ '</form>'.format( url=self.url(item), text=Markup.escape(self.text(item, attr_list)))
def post_item(name=None): if 'logged_in' in session: uid = session['user_id'] g.db = connect_db(app.config['USER_DB']) droits = get_droits(uid) g.db.close() if droits['adm'] == 1: if (request.method == 'POST'): g.db = connect_db(app.config['USER_DB']) cur = g.db.execute('select id from items where nam like ?', [request.form['nam']]) entries = [dict(name=row[0]) for row in cur.fetchall()] if len(entries) == 0: g.db.execute('insert into items (nam, price, recette, use_in, tooltip, tooltip_untouch, des, categorie) values (?, ?, ?, ?, ?, ?, ?, ?)', [request.form['nam'], request.form['prix'], request.form['recette'], request.form['use_in'], markdown.markdown(Markup.escape(request.form['tooltip'])), request.form['tooltip'], request.form['des'], request.form['categorie']]) g.db.commit() else: g.db.execute('update items set price = ?, recette = ?, use_in = ?, tooltip = ?, tooltip_untouch = ?, des = ?, categorie = ? where nam like ?', [request.form['prix'], request.form['recette'], request.form['use_in'], markdown.markdown(Markup.escape(request.form['tooltip'])), request.form['tooltip'], request.form['categorie'], request.form['des']]) g.db.commit() g.db.close() return redirect(url_for('item', name = request.form['nam'])) else: if name == None: return render_template('post_item.html') else: g.db = connect_db(app.config['USER_DB']) cur = g.db.execute('select * from items where nam like ?', [name]) entries = [dict(id_item=row[0], nam=row[1], prix=row[2], recette=row[3], use_in=row[4], tooltip=row[6], des=row[7], cat=row[8]) for row in cur.fetchall()] g.db.close() if (len(entries) == 0): return render_template('post_item.html') else: return render_template('post_item.html', entries = entries) return redirect(url_for('default'))
def signup(): """ регистрация пользователя """ error = None # проверяем доступна ли регистрация if 'REGISTRATION_ENABLED' in current_app.config: if current_app.config['REGISTRATION_ENABLED'] == False: # если регистрация отключена, и есть пользователи в БД, # то показываем форму логина if get_users_count(): return redirect(url_for('users.signin')) # возвращаем форму логина if request.method == 'GET': return render_template("users/signup.html") # остальное, метод POST, т.е. начинаем регистрацию # валидируем форму valid, error = signup_validate(request.form) if not valid: return render_template("users/signup.html", error=error) surname = None if 'surname' in request.form: surname = Markup.escape(request.form['surname']) name = None if 'name' in request.form: name = Markup.escape(request.form['name']) # регистрируем нового пользователя user = User(user_name=Markup.escape(request.form['username']), name=name, surname=surname, password='', email=Markup.escape(request.form['email']), enabled=True) user.set_password(request.form['password']) # если это первый созданный пользователь, то он # автоматом получает права администратора if not get_users_count(): user.roles = ['super_admin'] user.save() return redirect(url_for('users.signin'))
def inventory(): db = get_db() form=InventoryAddForm() if form.validate_on_submit(): item_name = Markup.escape(request.form['item']) # escape the search term before broadcasting it! price = Markup.escape(request.form['price']) db.execute("insert into items (itm_name, usr, prc) values(?, ?, ?);",[item_name,g.user.id, price]) db.commit() cur = db.execute("select itm_id, itm_name, prc from items where usr=?",[g.user.id]) items = cur.fetchall() return render_template("inventory.html", user=g.user, items=items, form=form)
def create_new_room(r, room_id, request): max_participants= request.form["participants"] try: max_participants= int(max_participants) except Exception as e: print("Exception: main.create_new_room:",e) max_participants= '' room_info={"room_kind":request.form["room_kind"], "room_title":Markup.escape(request.form["title"]), "max_participants":max_participants, "cur_participants":0, "open_time":str(time.time())} room_data={"room_seq":room_id, "room_title": room_info["room_title"], "room_kind": room_info["room_kind"], "open_time": room_info["open_time"], "max_participants": room_info["max_participants"], "voted_members":[], "out_dated":False} g.MongoDao.insert_room(room_data) if g.MongoDao.add_room_to_tag("tag_me", room_id, 0, 0)["updatedExisting"]== False: g.MongoDao.insert_tag("tag_me", room_id, 0, 0) if request.form["room_kind"]== "versus": room_data={"room_seq":room_id+"_supportA", "room_title": "", "room_kind": "support", "open_time": room_info["open_time"], "max_participants": room_info["max_participants"], "voted_members":[], "out_dated":False} g.MongoDao.insert_room(room_data) room_data={"room_seq":room_id+"_supportB", "room_title": "", "room_kind": "support", "open_time": room_info["open_time"], "max_participants": room_info["max_participants"], "voted_members":[], "out_dated":False} g.MongoDao.insert_room(room_data)
def comment_id(): user = fetch_id_from_userid(current_user.id) ili_id = request.args.get('ili_id', None) comment = request.args.get('comment', None) comment = str(Markup.escape(comment)) dbinsert = comment_ili_id(ili_id, comment, user) return jsonify(result=dbinsert)
def getSafeTweet(tweet): safeTweet = str(Markup.escape(tweet)) injectedTweet = '' # Inject <a href> for hashtags HASH_TAG_STATE = 1 IGNORE_NEXT_STATE = 2 state = 0 hashTagText = '' for c in safeTweet: if state == HASH_TAG_STATE: if (c < 'A' or c > 'Z') and (c < 'a' or c > 'z') and (c < '0' or c > '9'): injectedTweet += '<a href="/hashtag/%s">#%s</a>%s' % (hashTagText.lower(), hashTagText, c) state = 0 else: hashTagText += c elif state == IGNORE_NEXT_STATE: injectedTweet += c state = 0 else: if c == '#': hashTagText = '' state = HASH_TAG_STATE elif c == '&': injectedTweet += c state = IGNORE_NEXT_STATE else: injectedTweet += c # Close hash if still in hashTagState if state == HASH_TAG_STATE: injectedTweet += '<a href="/hashtag/%s">#%s</a>' % (hashTagText.lower(), hashTagText) state = 0 return Markup(injectedTweet)
def details(objectid): """ Displays entry details. """ from flask import Markup from pprint import pformat of = "hd" bwobject = BibWorkflowObject.query.filter( BibWorkflowObject.id == objectid ).first() workflow_object = Workflow.query.filter( Workflow.uuid == bwobject.id_workflow ).first() workflow_tasks = get_workflow_definition(workflow_object.name) formatted_data = bwobject.get_formatted_data(of) if isinstance(formatted_data, dict): formatted_data = pformat(formatted_data) if of and of in ("xm", "xml", "marcxml"): data = Markup.escape(formatted_data) else: data = formatted_data engine_log = BibWorkflowEngineLog.query.filter( BibWorkflowEngineLog.id_object == workflow_object.uuid ) return dict(entry=bwobject, log=engine_log, data_preview=data, workflow_tasks=workflow_tasks)
def comment(request): if request.method == "POST": form = CommentFrom(request.POST) if form.is_valid(): cdata = form.cleaned_data body = cdata['body'] post_id = int(cdata['post_id']) reco_id = cdata['reco_id'] safe_body = unicode(Markup.escape(body)) encoded_body = text_handle(safe_body, 'comment') post = Post.objects.get(id=post_id) try: reco = Comment.objects.get(id=reco_id) except: reco = None new_comment = Comment(post=post, user=request.user, reco=reco, body=safe_body, encoded_body=encoded_body, date=get_time(), is_active=True) new_comment.save() # inform @ re.sub(r"href='/u/\d+'", lambda x: inform(x.group(0)[9:-1], 'comment', new_comment.id), encoded_body) return HttpResponseRedirect(u'/post/'+unicode(post.id)+'#comment_area')
def blockcode(self, text, lang): if not lang: return '\n<pre><code>%s</code></pre>\n' % Markup.escape(text) lexer = pygments.lexers.get_lexer_by_name(lang, stripall=True) formatter = pygments.formatters.HtmlFormatter() rendered = pygments.highlight(text, lexer, formatter) return rendered
def quotedpost(self, forceurl=False, cutlines=False): escaped=Markup.escape(self.post) reg=re.compile(r">>\d+") ess=unicode(escaped) for m in set(reg.findall(ess)): qn=m.replace(">>","") qint=int(qn) np=Ipost.query.filter_by(id=qint) if np.count()!=0: if np.first().ithread==self.ithread and forceurl==False: ess=ess.replace(m, "<a href='#%s'>%s</a>"%(qn,m)) else: ess=ess.replace(m, "<a href='/thread/%d#%s'>%s</a>"%(np.first().ithread.id, qn, m)) ##idezes u=[] for s in ess.split("\n"): if s.startswith(">"): u.append("<span style='color: green'>"+s+"</span>") else: u.append(s) #elolnezeti sorok levagasa if cutlines and len(u)>app.config['PREVIEW_LINES']: visible=u[0:app.config['PREVIEW_LINES']] u=visible visible.append("<br><small class='threadinfo'>Comment too long</small>") ess="\n".join(u) return Markup(ess.replace("\n", "<br>"))
def upload(): # Get the name of the uploaded file file = request.files['file'] # Check if the file is one of the allowed types/extensions if file and allowed_file(file.filename): # Make the filename safe, remove unsupported chars filename = secure_filename(file.filename) # Move the file form the temporal folder to # the upload folder we setup file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) try: adaptive_resize(os.path.join(app.config['UPLOAD_FOLDER'], filename)) except: pass # Redirect the user to the uploaded_file route, whicuploadh # will basicaly show on the browser the uploaded file cmd = "./darknet yolo test cfg/cpuNet6.cfg cpuNet6.weights %s%s" % (app.config['UPLOAD_FOLDER'], filename) try: stdout_result = subprocess.check_output(cmd, shell=True) info = stdout_result.split(':')[1:] info = ''.join(info) text = "" for line in info.split('\n'): text += Markup.escape(line) + Markup('<br />') return render_template('result.html', filename=os.path.join(app.config['OUTPUT_FOLDER'], os.path.basename(filename).split('.')[0] + '.png'), result_text=text) except: return render_template('result.html', filename=os.path.join(app.config['UPLOAD_FOLDER'], filename), result_text="Unexpected error occured, terminate detection.") #return redirect(url_for('uploaded_file', filename=filename)) return "Invalid file or format"
def testFunction(domain_url): try: address = str(dns.resolver.query(domain_url)[0]) status = 0 briefing = "Could locate domain " + domain_url + " at " + address + "!" message = briefing return (status, briefing, message, None) except dns.resolver.NXDOMAIN: status = 1 briefing = "Could not locate " + domain_url + "!" message = "We were unable to find your domain " + domain_url + "." return (status, briefing, message, None) except Exception, e: status = 2 briefing = "A problem happened while attempting to locate your domain " + domain_url + "!" message = "Something odd happened while we were trying to locate your domain " + domain_url + "!" e_type, e_value, e_trace = sys.exc_info() e_type = Markup.escape(str(type(e))).__str__() # message += "<br/>This is the exception we got: {"+str(e)+"}" message += "<br/>This is the exception we got: {type:%s, value:%s}" % (e_type, e_value) message += "<br/>It is probably a temporary issue with domain " + domain_url + "." message += "<br/>But it could also be a bug in our Inspector. Let us know at <a href='https://github.com/buddycloud/buddycloud-tests-framework/issues'>our issue tracker</a> if you think so." return (status, briefing, message, None)
def musical(s): return s + Markup(' ♫')
def desserts_edit(recipe_id, slugUrl): """ Update recipe in database. Inject all existing data from the recipe back into the form. """ if request.method == "GET": recipe = get_recipe(recipe_id) # generate dropdown lists from helper functions allergen_list = dropdown_allergens() dessert_list = dropdown_dessert_type() measurement_list = dropdown_measurement() # generate ingredient list items amount_list = [amount for amount in recipe.get("ingredient_amount")] unit_list = [unit for unit in recipe.get("ingredient_measurement")] ingredient_list = ([ ingredient for ingredient in recipe.get("ingredient_name") ]) # zip the new lists into a single master list ingredients_list = zip(amount_list, unit_list, ingredient_list) return render_template("desserts_edit.html", recipe=recipe, allergens=allergen_list, desserts=dessert_list, measurements=measurement_list, ingredients=ingredients_list, recipe_id=recipe_id, slugUrl=slugUrl) """ Push the edits of the recipe to the collection on submit. """ if request.method == "POST": recipe = get_recipe(recipe_id) # get today's date and date recipe was last edited today = datetime.now().strftime("%d %B, %Y") last_edit = int(datetime.now().strftime("%Y%m%d")) # get non-editable values get_author = recipe.get("author") get_date_added = recipe.get("date_added") get_views = recipe.get("views") get_user_favs = recipe.get("user_favs") # get and convert total time hours = int(request.form.get("total_hrs")) * 60 if request.form.get( "total_hrs") else "" total_time = int( request.form.get("total_mins")) + hours if hours else int( request.form.get("total_mins")) # slugify url to be user-friendly slugUrl = slugify(request.form.get("recipe_name")) # push form data to recipe on submit recipes_collection.update({"_id": ObjectId(recipe_id)}, { "recipe_name": request.form.get("recipe_name"), "recipe_slug": slugUrl, "description": request.form.get("description"), "dessert_type": request.form.get("dessert_type"), "ingredient_amount": request.form.getlist("ingredient_amount"), "ingredient_measurement": request.form.getlist("ingredient_measurement"), "ingredient_name": request.form.getlist("ingredient_name"), "directions": request.form.getlist("directions"), "total_hrs": request.form.get("total_hrs"), "total_mins": request.form.get("total_mins"), "total_time": total_time, "allergens": request.form.getlist("allergens"), "img_src": request.form.get("img_src"), "author": get_author, "date_added": get_date_added, "date_updated": today, "last_edit": last_edit, "views": get_views, "user_favs": get_user_favs }) flash( Markup(f"<i class='far fa-check-circle green-text'></i>\ Your recipe has been updated successfully!")) return redirect( url_for("recipes.desserts_recipe", recipe_id=recipe_id, slugUrl=slugUrl))
def get_name_url_html(self): return Markup('<a href="{}">{}</a>'.format(self.get_url(), self.name))
def error_page(msg): form_token = gen_form_token(login_session, "edit-directory") return render_template("edit-directory.html", is_new = False, form_token = form_token, target_dir = target_dir, users = users, message_contents = Markup('<p class="error">') + msg + Markup('</p>'))
def pop_tab_link(self): return Markup(f""" <a href="/superset/sqllab?savedQueryId={self.id}"> <i class="fa fa-link"></i> </a> """)
def content(self): return Markup('\n\n'.join('<p>%s</p>' % line for line in self.body.splitlines()))
def dashboard_link(self): title = escape(self.dashboard_title) return Markup('<a href="{self.url}">{title}</a>'.format(**locals()))
def slice_link(self): url = self.slice_url name = escape(self.slice_name) return Markup('<a href="{url}">{name}</a>'.format(**locals()))
"markupsafe == 0.23"] , force_create = True) sys.path.append(ENV_NAME + "/site-packages") from flask import Flask, Markup from jinja2 import Template app = Flask(__name__) @app.route('/') def test_template(): t = Template("Hello, World!: {% for n in range(1,10) %}{{n}} " "{% endfor %}") return t.render() if __name__ == '__main__': app.config['TESTING'] = True assert isinstance(app, Flask) server = multiprocessing.Process(target=app.run) server.start() time.sleep(1) f = urllib2.urlopen("http://127.0.0.1:5000/", timeout=1) s = f.read() assert 'Hello, World!: 1 2 3 4 5 6 7 8 9' in s server.terminate() server.join() m1 = Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>' m2 = Markup.escape('<blink>hacker</blink>') m3 = Markup('<em>Marked up</em> » HTML').striptags() print 'PASSED'
def create_strategy(): if not (session.get('USERNAME') and session['USERNAME']): flash('使用此功能必須先登入。', 'danger') return redirect('/login') if session['USERNAME'] in black_list: flash('我們已經暫停您建立策略的權利,有疑問請洽[email protected]', 'danger') return redirect('/') print('last_creation_time: ', session['last_creation_time']) if time.time() - session['last_creation_time'] < 30: print('Strategy ', request.form['strategy_name'], ' rejected to create because of insufficient time gap.') flash('每兩次建立策略須間隔200秒', 'danger') return redirect('/') if request.method == 'GET': tw = request.values.get('tw') tw_digit = 1 if tw=='true' else 0 if tw=='false' else None if request.method == 'POST': strategy_name = request.form['strategy_name'] create_date = datetime.strftime(datetime.now() + timedelta(hours=8), '%Y/%m/%d %H:%M:%S.%f') session['last_creation_time'] = time.time() if strategy_name == '': flash('請取一個名字', 'danger') return render_template('create_strategy.html', asset_candidates=asset_candidates if tw=='false' else asset_candidates_tw if tw=='true' else None, tw=tw) competition = request.form['competition'] tw = request.form['tw'] tw_digit = 1 if tw=='true' else 0 if tw=='false' else None tickers = sorted(list(set(request.form.getlist('asset_ticker')))) print('The list of assets: ', tickers) # Turn off progress printing solvers.options['show_progress'] = False start_dates = [datetime(2015, 1, 1), datetime(2015, 4, 1), datetime(2015, 7, 1), datetime(2015, 10, 1), datetime(2016, 1, 1), datetime(2016, 4, 1), datetime(2016, 7, 1), datetime(2016, 10, 1), datetime(2017, 1, 1), datetime(2017, 4, 1), datetime(2017, 7, 1), datetime(2017, 10, 1), datetime(2018, 1, 1), datetime(2018, 4, 1), datetime(2018, 7, 1), datetime(2018, 10, 1), datetime(2019, 1, 1), datetime(2019, 4, 1), datetime(2019, 7, 1), datetime(2019, 10, 1), datetime(2020, 1, 1), datetime(2020, 4, 1), datetime(2020, 7, 1), datetime(2020, 10, 1), datetime(2021, 1, 1), datetime(2021, 4, 1) ] if tw=='false': all_data = pd.read_csv('data_for_trading_platform.csv') elif tw=='true': all_data = pd.read_csv('data_for_trading_platform_tw.csv') else: all_data = None all_data['Date'] = pd.to_datetime(all_data['Date'], format='%Y-%m-%d') def stockpri(ticker, start, end): data = all_data[ (all_data['Ticker']==ticker) & (all_data['Date']>=start) & (all_data['Date']<=end) ] data.set_index('Date', inplace=True) data = data['Adj Close'] return data portfolio_value = pd.Series([100]) optimal_weights = None hist_return_series = pd.DataFrame(columns=['quarter', 'quarterly_returns']) for i in range(len(start_dates)-3): ### Take 6 months to backtest ### start = start_dates[i] end = start_dates[i+2] data = pd.DataFrame({ ticker: stockpri(ticker, start, end) for ticker in tickers }) data = data.dropna() returns = data.pct_change() + 1 returns = returns.dropna() log_returns = np.log(data.pct_change() + 1) log_returns = log_returns.dropna() if log_returns.empty: continue mu = np.exp(log_returns.mean()*252).values # Markowitz frontier profit = np.linspace(np.amin(mu), np.amax(mu), 100) frontier = [] w = [] if len(tickers) >= 3: for p in profit: # Problem data. n = len(tickers) S = matrix(log_returns.cov().values*252) pbar = matrix(0.0, (n,1)) # Gx <= h G = matrix(0.0, (2*n,n)) G[::(2*n+1)] = 1.0 G[n::(2*n+1)] = -1.0 # h = matrix(1.0, (2*n,1)) h = matrix(np.concatenate((0.5*np.ones((n,1)), -0.03*np.ones((n,1))), axis=0)) A = matrix(np.concatenate((np.ones((1,n)), mu.reshape((1,n))), axis=0)) b = matrix([1, p], (2, 1)) # Compute trade-off. res = qp(S, -pbar, G, h, A, b) if res['status'] == 'optimal': res_weight = res['x'] s = math.sqrt(dot(res_weight, S*res_weight)) frontier.append(np.array([p, s])) w.append(res_weight) elif len(tickers) == 2: for p in profit: S = log_returns.cov().values*252 res_weight = [1 - (p-mu[0])/(mu[1]-mu[0]), (p-mu[0])/(mu[1]-mu[0])] if (res_weight[0] < 0.03) or (res_weight[0] > 0.97): continue s = math.sqrt(np.matmul(res_weight, np.matmul(S, np.transpose(res_weight)))) frontier.append(np.array([p, s])) w.append(res_weight) frontier = np.array(frontier) if frontier.shape == (0,): continue x = np.array(frontier[:, 0]) y = np.array(frontier[:, 1]) frontier_sharpe_ratios = np.divide(x-1, y) optimal_portfolio_index = np.argmax(frontier_sharpe_ratios) optimal_weights = w[optimal_portfolio_index] ### paper trade on the next three months ### start = start_dates[i+2] end = start_dates[i+3] data = pd.DataFrame({ ticker: stockpri(ticker, start, end) for ticker in tickers }) data = data.dropna() returns = data.pct_change() + 1 returns = returns.dropna() log_returns = np.log(data.pct_change() + 1) log_returns = log_returns.dropna() portfolio_cum_returns = np.dot(returns, optimal_weights).cumprod() portfolio_value_new_window = portfolio_value.iloc[-1].item() * pd.Series(portfolio_cum_returns) portfolio_value_new_window.index = pd.to_datetime(returns.index, format='%Y-%m-%d') portfolio_value = portfolio_value.append(portfolio_value_new_window) # produce quarterly return hist_return_series.loc[len(hist_return_series)] = [str(start.year)+'Q'+str((start.month+2)//3), portfolio_cum_returns[-1]-1] if optimal_weights == None: sharpe_ratio = avg_annual_return = annual_volatility = max_drawdown = ten_day_var = 0 optimal_weights = [0, ]*len(tickers) hist_returns = None conn = psycopg2.connect(database=POSTGRESQL_DATABASE, user=POSTGRESQL_USER) cur = conn.cursor(cursor_factory = psycopg2.extras.DictCursor) cur.execute("""insert into strategy (strategy_name, author, create_date, sharpe_ratio, return, volatility, max_drawdown, tw, competition, hist_returns, ten_day_var) values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) returning strategy_id;""", (strategy_name, session['USERNAME'], create_date, sharpe_ratio, avg_annual_return, annual_volatility, max_drawdown, tw_digit, competition, hist_returns, ten_day_var ) ) strategy_id = cur.fetchone()[0] conn.commit() # record the list of tickers into database # strategy_id = cur.execute('select * from strategy where create_date=?', [create_date]).fetchone()['strategy_id'] for i in range(len(tickers)): cur.execute('insert into assets_in_strategy (strategy_id, asset_ticker, weight) values (%s, %s, %s)', (strategy_id, tickers[i], optimal_weights[i])) conn.commit() cur.close() conn.close() print('Strategy_id ' + str(strategy_id) + ' optimization fails.') flash('無資料或無法畫出馬可維茲邊界,請換一個組合', 'danger') return render_template('create_strategy.html', asset_candidates=asset_candidates if tw=='false' else asset_candidates_tw if tw=='true' else None, tw=tw) avg_annual_return = np.exp(np.log(portfolio_value.pct_change() + 1).mean() * 252) - 1 annual_volatility = portfolio_value.pct_change().std() * math.sqrt(252) sharpe_ratio = avg_annual_return/annual_volatility max_drawdown = - np.amin(np.divide(portfolio_value, np.maximum.accumulate(portfolio_value)) - 1) ten_day_var = ten_day_VaR(portfolio_value) print('Sharpe ratio: ', sharpe_ratio, ', Return: ', avg_annual_return, ', Volatility: ', annual_volatility, ', Maximum Drawdown: ', max_drawdown) # hist_return_series.set_index('quarter', inplace=True) hist_returns = pickle.dumps(hist_return_series) conn = psycopg2.connect(database=POSTGRESQL_DATABASE, user=POSTGRESQL_USER) cur = conn.cursor(cursor_factory = psycopg2.extras.DictCursor) cur.execute("""insert into strategy (strategy_name, author, create_date, sharpe_ratio, return, volatility, max_drawdown, tw, competition, hist_returns, ten_day_var) values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) returning strategy_id;""", (strategy_name, session['USERNAME'], create_date, sharpe_ratio, avg_annual_return, annual_volatility, max_drawdown, tw_digit, competition, hist_returns, ten_day_var ) ) strategy_id = cur.fetchone()[0] conn.commit() # record the list of tickers into database # strategy_id = cur.execute('select * from strategy where create_date=?', [create_date]).fetchone()['strategy_id'] for i in range(len(tickers)): cur.execute('insert into assets_in_strategy (strategy_id, asset_ticker, weight) values (%s, %s, %s)', (strategy_id, tickers[i], optimal_weights[i])) conn.commit() cur.close() conn.close() # fig, ax = plt.subplots() # hist_return_series.hist(column='quarterly_returns', by='quarter', ax=ax) hist_return_plot = hist_return_series.plot.bar(x='quarter', y='quarterly_returns').get_figure() plt.tight_layout() plt.xticks(rotation=45) hist_return_plot.savefig('static/img/quarterly_returns/'+str(strategy_id)+'.png') plt.close() print(portfolio_value.head()) print(portfolio_value.tail()) plt.xticks(rotation=45) plt.plot(portfolio_value.iloc[1:]) plt.savefig('static/img/portfolio_values/'+str(strategy_id)+'.png') plt.close() print('Strategy_id ' + str(strategy_id) + ' optimization succeeds.') flash(Markup('回測已完成,詳情請<a href="/post_page?post_id=' + str(strategy_id) + '">點這裡查看</a>。'), 'success') return render_template('create_strategy.html', asset_candidates=asset_candidates if tw=='false' else asset_candidates_tw if tw=='true' else None, tw=tw)
#: Use SSL for some URLs USE_SSL = False #: Twitter integration OAUTH_TWITTER_KEY = '' OAUTH_TWITTER_SECRET = '' #: GitHub integration OAUTH_GITHUB_KEY = '' OAUTH_GITHUB_SECRET = '' #: Recaptcha for the registration form RECAPTCHA_USE_SSL = USE_SSL RECAPTCHA_PUBLIC_KEY = '' RECAPTCHA_PRIVATE_KEY = '' RECAPTCHA_OPTIONS = '' #: SMS gateways SMS_SMSGUPSHUP_MASK = '' SMS_SMSGUPSHUP_USER = '' SMS_SMSGUPSHUP_PASS = '' #: Messages (text or HTML) MESSAGE_FOOTER = Markup('Copyright © <a href="http://hasgeek.com/">HasGeek</a>. Powered by <a href="https://github.com/hasgeek/lastuser" title="GitHub project page">Lastuser</a>, open source software from <a href="https://github.com/hasgeek">HasGeek</a>.') USERNAME_REASON = '' EMAIL_REASON = 'Please provide an email address to complete your profile' BIO_REASON = '' ORG_NAME_REASON = u"Your company’s name as it will appear in the URL. Letters, numbers and dashes only" ORG_TITLE_REASON = u"Your organization’s given name, preferably without legal suffixes" ORG_DESCRIPTION_REASON = u"A few words about your organization (optional). Plain text only"
def link(self) -> Markup: name = escape(self.name) anchor = f'<a target="_blank" href="{self.explore_url}">{name}</a>' return Markup(anchor)
def _user_link(self, user): if not user: return '' url = '/superset/profile/{}/'.format(user.username) return Markup('<a href="{}">{}</a>'.format(url, escape(user) or ''))
def link(self): return Markup('<a href="' + self.url + '">') + Markup.escape(self.name) + Markup('</a>')
def allow_subscripts_globally(text): text = sub('~(\S+)~', lambda match: Markup(f'<sub>{match.group(1)}</sub>'), text) return text
def render_markdown(content): return Markup(markdown.markdown(content))
posts = [Post(post) for post in post_data] for post in posts: if post.slug in self.by_slug: raise RuntimeError("slugs must be unique") self.by_slug[post.slug] = post self.by_date.extend(posts) self.by_date.sort(key=attrgetter('created'), reverse=True) store = PostStore() with app.open_resource('posts.yaml') as fd: post_data = yaml.load_all(fd) store.add_posts(post_data) ABOUT_TEXT = Markup('<p>This is a demonstration of Flask-Themes.</p>') # themes def render(template, **context): theme = session.get('theme', app.config['DEFAULT_THEME']) return render_theme_template(theme, template, **context) # views @app.route('/') def index(): posts = store.by_date[:3]
def client(LCD): with open(str("./" + LCD + ".txt"), 'r') as file: image = file.readline() image = "/static/" + image image = Markup(image) return render_template("display.html", **locals())
def get_file_ultrabad(tofile): f = open(tofile, 'r') # <- file path injection here! data = escape(f.read()) pre = Markup('<html><body>File data: <pre>') post = Markup('</pre>File ends.</body></html>') return pre + data + post
def safe_markdown(text): renderer = HtmlRenderer() md = Markdown(renderer, extensions=('fenced-code', )) return Markup(safe_clean(md(text)))
def post_edit_directory(dir_id): ''' ディレクトリ編集受信処理 :param int dir_id: ディレクトリ ID :return: ディレクトリ編集フォームテンプレート ''' # ログイン状態チェック login_session, redirect_obj = verify_login_session() if not login_session: return redirect_obj # ユーザーが管理者でなければ forbidden acc = db.get_account(login_session["user_id"]) if not acc or not acc["is_admin"]: return abort(403) # 受信データサイズをチェック (でかすぎる場合はけんもほろろに Bad Request) if request.content_length > int(conf["Security"]["MaxFormLength"]): return abort(400) # ID のディレクトリ情報を取得 (取れない場合は 404) target_dir = db.get_directory(dir_id) if not target_dir: return abort(404) raw_form = urllib.parse.parse_qsl(request.get_data(as_text = True)) # フォームトークンの検証に失敗した場合は Bad Request form_token = request.form["tk"] if not verify_form_token(login_session, "edit-directory", form_token): return abort(400) dir_name = request.form["nm"].strip() summary = request.form["sm"].strip() expires_days = int(request.form["ed"]) target_dir.update({ "directory_name": dir_name, "summary": summary, "expires_days": expires_days, }) permissions = [n[1] for n in raw_form if n[0] == "pm"] users = db.get_all_accounts() for user in users: user["allow"] = user["user_id"] in permissions def error_page(msg): form_token = gen_form_token(login_session, "edit-directory") return render_template("edit-directory.html", is_new = False, form_token = form_token, target_dir = target_dir, users = users, message_contents = Markup('<p class="error">') + msg + Markup('</p>')) if not permissions: return error_page("参照権限ユーザーを一人以上選択してください。") try: directory = db.update_directory(dir_id, dir_name, expires_days, summary) except TmpboxDBDuplicatedException as exc: return error_page("".join(exc.args)) db.update_permission(dir_id, permissions) return render_template("edit-directory.html", is_new = False, form_token = form_token, target_dir = directory, users = users, message_contents = Markup('<p class="info">ディレクトリの情報を更新しました。</p>'))
def error_page(msg): return render_template("edit-account.html", is_new = True, target_user = { "user_id": user_id, "display_name": display_name }, message_contents = Markup('<p class="error">') + msg + Markup('</p>'))
def post_new_directory(): ''' 新規ディレクトリ登録受信処理 :return: 登録完了のアナウンスページ ''' # ログイン状態チェック login_session, redirect_obj = verify_login_session() if not login_session: return redirect_obj # ユーザーが管理者でなければ forbidden acc = db.get_account(login_session["user_id"]) if not acc or not acc["is_admin"]: return abort(403) # 受信データサイズをチェック (でかすぎる場合はけんもほろろに Bad Request) if request.content_length > int(conf["Security"]["MaxFormLength"]): return abort(400) raw_form = urllib.parse.parse_qsl(request.get_data(as_text = True)) # フォームトークンの検証に失敗した場合は Bad Request form_token = request.form["tk"] if not verify_form_token(login_session, "new-directory", form_token): return abort(400) dir_name = request.form["nm"].strip() summary = request.form["sm"].strip() expires_days = int(request.form["ed"]) permissions = [n[1] for n in raw_form if n[0] == "pm"] def error_page(msg): users = db.get_all_accounts() for user in users: user["allow"] = user["user_id"] in permissions form_token = gen_form_token(login_session, "new-directory") return render_template("edit-directory.html", is_new = True, form_token = form_token, target_dir = { "directory_name": dir_name, "summary": summary, "expires_days": expires_days, }, users = users, message_contents = Markup('<p class="error">') + msg + Markup('</p>')) if dir_name == "": return error_page("ディレクトリ名を入力してください。") if not permissions: return error_page("参照権限ユーザーを一人以上選択してください。") try: dir_id = db.register_directory(dir_name, expires_days, summary) except TmpboxDBDuplicatedException as exc: return error_page(''.join(exc.args)) db.update_permission(dir_id, permissions) return render_template("easy-info.html", summary = "ディレクトリ作成完了", content = Markup('<p>ディレクトリ <code class="directory">') + dir_name + Markup("</code> の作成に成功しました。"), prev_url = "/admin", prev_page = "管理者ページ")
def download(self): return Markup('<a href="' + url_for('ProjectFilesModelView.download', filename=str(self.file)) + '">Download</a>')
class SliceMixin: # pylint: disable=too-few-public-methods list_title = _("Charts") show_title = _("Show Chart") add_title = _("Add Chart") edit_title = _("Edit Chart") can_add = False search_columns = ( "slice_name", "description", "viz_type", "datasource_name", "owners", ) list_columns = [ "slice_link", "viz_type", "datasource_link", "creator", "modified" ] order_columns = ["viz_type", "datasource_link", "modified"] edit_columns = [ "slice_name", "description", "viz_type", "owners", "dashboards", "params", "cache_timeout", ] base_order = ("changed_on", "desc") description_columns = { "description": Markup("The content here can be displayed as widget headers in the " "dashboard view. Supports " '<a href="https://daringfireball.net/projects/markdown/"">' "markdown</a>"), "params": _("These parameters are generated dynamically when clicking " "the save or overwrite button in the explore view. This JSON " "object is exposed here for reference and for power users who may " "want to alter specific parameters."), "cache_timeout": _("Duration (in seconds) of the caching timeout for this chart. " "Note this defaults to the datasource/table timeout if undefined."), } base_filters = [["id", SliceFilter, lambda: []]] label_columns = { "cache_timeout": _("Cache Timeout"), "creator": _("Creator"), "dashboards": _("Dashboards"), "datasource_link": _("Datasource"), "description": _("Description"), "modified": _("Last Modified"), "owners": _("Owners"), "params": _("Parameters"), "slice_link": _("Chart"), "slice_name": _("Name"), "table": _("Table"), "viz_type": _("Visualization Type"), } add_form_query_rel_fields = { "dashboards": [["name", DashboardFilter, None]] } edit_form_query_rel_fields = add_form_query_rel_fields
def modified(self): s = humanize.naturaltime(datetime.now() - self.changed_on) return Markup('<span class="no-wrap">{}</span>'.format(s))
def safe_clean(text): tags = ['b', 'i', 'font', 'br', 'blockquote', 'div', 'h2', 'a', 'p'] attrs = {'*': ['style', 'id', 'class'], 'font': ['color'], 'a': ['href']} styles = ['color'] return Markup(clean(text, tags=tags, attributes=attrs, styles=styles))
def desserts_new(): """ Create recipe for database. Inject all form data to new recipe document on submit. """ if request.method == "GET": allergen_list = dropdown_allergens() dessert_list = dropdown_dessert_type() measurement_list = dropdown_measurement() return render_template("desserts_new.html", allergens=allergen_list, desserts=dessert_list, measurements=measurement_list) if request.method == "POST": # get today's date and date recipe was last edited today = datetime.now().strftime("%d %B, %Y") last_edit = int(datetime.now().strftime("%Y%m%d")) # get user / author details session_user = get_user_lower(session["user"])["username"] author = users_collection.find_one({"username": session_user})["_id"] # get and convert total time hours = int(request.form.get("total_hrs")) * 60 if request.form.get( "total_hrs") else "" total_time = int( request.form.get("total_mins")) + hours if hours else int( request.form.get("total_mins")) # slugify url to be user-friendly slugUrl = slugify(request.form.get("recipe_name")) # get form data prior to submitting submit = { "recipe_name": request.form.get("recipe_name"), "recipe_slug": slugUrl, "description": request.form.get("description"), "dessert_type": request.form.get("dessert_type"), "ingredient_amount": request.form.getlist("ingredient_amount"), "ingredient_measurement": request.form.getlist("ingredient_measurement"), "ingredient_name": request.form.getlist("ingredient_name"), "directions": request.form.getlist("directions"), "total_hrs": request.form.get("total_hrs"), "total_mins": request.form.get("total_mins"), "total_time": total_time, "allergens": request.form.getlist("allergens"), "img_src": request.form.get("img_src"), "author": author, "date_added": today, "date_updated": today, "last_edit": last_edit, "views": 0, "user_favs": 0 } # get the new _id being created on submit newID = recipes_collection.insert_one(submit) # add recipe _id to user's recipe list users_collection.update_one( {"_id": ObjectId(author)}, {"$push": { "user_recipes": newID.inserted_id }}) flash( Markup(f"<i class='far fa-check-circle green-text'></i>\ Sounds delicious! Thanks for adding this recipe!")) # if selected, add recipe to user-favs as well if request.form.get("add_favs"): users_collection.update_one( {"_id": ObjectId(author)}, {"$push": { "user_favs": newID.inserted_id }}) # increase number of favorites on this recipe by +1 recipes_collection.update_one({"_id": newID.inserted_id}, {"$inc": { "user_favs": 1 }}) return redirect( url_for("recipes.desserts_recipe", recipe_id=newID.inserted_id, slugUrl=slugUrl))
" t": (1, 2, 3) }, { " t__": b"a" }, { " di": " di" }, { "x": (1, 2, 3), "y": 4 }, (1, 2, 3), [(1, 2, 3)], b"\xff", Markup("<html>"), uuid4(), datetime.utcnow().replace(microsecond=0), ), ) def test_dump_load_unchanged(data): s = TaggedJSONSerializer() assert s.loads(s.dumps(data)) == data def test_duplicate_tag(): class TagDict(JSONTag): key = " d" s = TaggedJSONSerializer() pytest.raises(KeyError, s.register, TagDict)
def changed_on_(self): return Markup( '<span class="no-wrap">{}</span>'.format(self.changed_on))