Package: qree
QreeQree (read 'Curie') is a tiny but mighty Python templating engine, geared toward HTML. 'Qree' is short for:Quote,replace,exec(),eval().The entire module is under 200 lines. Instead of using regular expressions or PEGs, Qree relies on Python'sexec()andeval(). Thus, it supportsall language features, out of the box. For more on Qree's internals, please see:Build Your Own Python Template Engine!!! Warning:DoNOTrender untrusted templates. As Qree useseval(), rendering untrusted templates is equivalent to giving untrusted entities access to your entire systems.Installationpip install qreeAlternatively, just downloadqree.pyinto your project directory.Text InterpolationUse{{: expression :}}for HTML-escaped interpolation, or{{= expression =}}for interpolationwithoutescaping. The latter issusceptible to XSS, so please be careful. Here are a few quick examples:1. Hello, World!:qree.renderStr("<h2>Hello, {{: data :}}",data="World!")# Output: <h2>Hello, World!</h2>2. Using Expressions:qree.renderStr("<h2>Mr. {{: data.title() + '!' :}}",data="bond")# Output: <h2>Mr. Bond!</h2>3.  HTML Escaping:qree.renderStr("Mr. {{: data :}}",data="<b> Villain </b>")# Output: Mr. &lt;b&gt; Villain &lt;/b&gt;4. Without Escaping:qree.renderStr("Mr. {{= data =}}",data="<b> Villain </b>")# Output: Mr. <b> Villain </b>5. Longer Example:qree.renderStr("""<!doctype html><html><head><title>{{: data["title"].title() :}}</title></head><body><h1>{{: data["title"].title() :}}</h1><pre>{{: data["body"] :}}</pre></body></html>""",data={"title":"Lorem Ipsum","body":"Lorem ipsum dolor sit amet, ... elit.",})# Output:<!doctype html><html><head><title>Lorem Ipsum</title></head><body><h1>Lorem Ipsum</h1><pre>Lorem ipsum dolor sit amet, ... elit.</pre></body></html>Python CodeAny line beginning with@=is treated as Python code. (Preceding whitespace is ignored.)  You can writeany codeyou wish, as Qree supports all language features. You can define variables, import modules, make assertions etc. For example:Leap Year Detection (withlambda):tplStr="""@= isLeap = lambda n: (n % 400 == 0) or (n % 100 != 0 and n % 4 == 0)@= isOrIsNot = "IS" if isLeap(data['year']) else "is NOT"The year {{: data['year'] :}} {{: isOrIsNot :}} a leap year."""qree.renderStr(tplStr,data={"year":2000})# Output: The year 2000 IS a leap year.qree.renderStr(tplStr,data={"year":2001})# Output: The year 2001 is NOT a leap year.Python IndentationPython is an indented language. Use the special tags@{and@}for respectively indicating indentation and de-indentation to Qree. When used, such a tagshould appear by itself on a separate line, ignoring whitespace and trailing Python comments. For example:Leap Year Detection (withdef):tplStr="""@= def isLeap (n):@{@= if n % 400 == 0: return True;@= if n % 100 == 0: return False;@= return n % 4 == 0;@}@= isOrIsNot = "IS" if isLeap(data['year']) else "is NOT"The year {{: data['year'] :}} {{: isOrIsNot :}} a leap year."""qree.renderStr(tplStr,data={"year":2000})# Output: The year 2000 IS a leap year.qree.renderStr(tplStr,data={"year":2001})# Output: The year 2001 is NOT a leap year.FizzBuzz ExampleFizzBuzz is a popular programming assignment. The idea is to print consecutive numbers per line, but instead to print'Fizz'for multiples of 3,'Buzz'for multiples of 5, and'FizzBuzz'for multiples of 3 and 5.qree.renderStr("""@= for n in range(1, data+1):@{@= if n % 15 == 0:@{FizzBuzz@}@= elif n % 3 == 0:@{Fizz@}@= elif n % 5 == 0:@{Buzz@}@= else:@{{{: n :}}@}@}""",data=20)# Output:1
        2
        Fizz
        4
        Buzz
        Fizz
        7
        8
        Fizz
        Buzz
        11
        Fizz
        13
        14
        FizzBuzz
        16
        17
        Fizz
        19
        BuzzThedataVariableBy default, data passed via thedataparameter is available in the template as thedatavariable. However, if you'd like to change the variable name, you may do so via thevariableparameter. For example:qree.renderStr("Hi {{: name :}}!",data="Jim",variable="name")# Output: Hi Jim!Template FilesIt's always convenient to store templates using separate files. To work with files, useqree.renderPath(tplPath, data, ...)instead ofqree.renderStr(tplStr, data, ...).Let's say you have the following directory structure:- app.py
- qree.py
- views/
        - homepage.htmlHere'shomepage.html:<doctypehtml><html><head><title>{{: data['title'] :}}</title></head><body><h2>{{: data['title'] :}}</h2><pre>{{: data['body'] :}}</pre></body><html>Inapp.py, you could have the following snippet:defserve_homepage():returnqree.renderPath("./views/homepage.html",data={"title":"The TITLE Goes Here!","body":"And the body goes here ...",});Which would be equivalent to:defserve_homepage():withopen("./views/homepage.html","r")asf:returnqree.renderStr(f.read(),data={"title":"The TITLE Goes Here!","body":"And the body goes here ...",});In either case, the output would be:<doctypehtml><html><head><title>The TITLE Goes Here!</title></head><body><h2>The TITLE Goes Here!</h2><pre>And the body goes here ...</pre></body><html>Quick PlugQree built and maintained by the folks atPolydojo, Inc., led bySumukh Barve. If your team is looking for a simple project management tool, please check out our latest product:BoardBell.com.Template NestingSince templates can include any Python code, you can callqree.renderPath()from within a template! Consider the following directory structure:- app.py
- qree.py
- views/
        - header.html
        - homepage.html
        - footer.htmlWithheader.html:<headerclass="header"><ahref="/link1">Link 1</a><ahref="/link2">Link 2</a><ahref="/link3">Link 3</a></header>And similarly,footer.html:<footerclass="footer"><ahref="/linkA">Link A</a><ahref="/linkB">Link B</a><ahref="/linkC">Link C</a></footer>Now, you can useheader.htmlandfooter.htmlinhomepage.html:@= import qree;
@= import qree;<doctypehtml><html><head><title>{{: data['title'] :}}</title></head><body>{{= qree.renderPath("./test-views/header.html", data=None) =}}<h2>{{: data['title'] :}}</h2><pre>{{: data['body'] :}}</pre>{{= qree.renderPath("./test-views/footer.html", data=None) =}}</body><html>And, as before, the snippet inapp.py:defserve_homepage():returnqree.renderPath("./views/homepage.html",data={"title":"The TITLE Goes Here!","body":"And the body goes here ...",});The output is:<doctypehtml><html><head><title>... TITLE 2 ...</title></head><body><headerclass="header"><ahref="/link1">Link 1</a><ahref="/link2">Link 2</a><ahref="/link3">Link 3</a></header><h2>... TITLE 2 ...</h2><pre>... BODY 2 ...</pre><footerclass="footer"><ahref="/linkA">Link A</a><ahref="/linkB">Link B</a><ahref="/linkC">Link C</a></footer></body><html>In the above example, we explicitly passeddata=Noneto each nested template. We could've passed any value. We could've even ignored thedataparameter, as it defaults toNoneanyway.Custom Tags (viatagMap)Default tags like{{:,:}},@=, etc. can each be customized via thetagMapparameter. UsingtagMap, just supply your desired tag as the value against the default tag as key. A few examples follow:1.[[:Square:]]Brackets Instead Of{{:Braces:}}qree.renderStr(tplStr="Hello, [[: data.title().rstrip('!') + '!' :]]",data="world",tagMap={"{{:":"[[:",":}}":":]]","{{=":"[[=",# <-- Not directly used in this example."=}}":"=]]",# <---^})# Output: Hello, World!2. Percentage Sign For Code Blocks (%vs@)tplStr="""%= isLeap = lambda n: (n % 400 == 0) or (n % 100 != 0 and n % 4 == 0)%= isOrIsNot = "IS" if isLeap(data['year']) else "is NOT"The year {{: data['year'] :}} {{: isOrIsNot :}} a leap year."""qree.renderStr(tplStr,data={"year":2020},tagMap={"@=":"%=","@{":"%{",# <-- Not directly used in this example."@}":"%}",# <--^})# Output: The year 2020 IS a leap year.DefaulttagMap:The default values for each of the tags is as specified in the dict below.{"@=":"@=","@{":"@{","@}":"@}","{{=":"{{=","=}}":"=}}","{{:":"{{:",":}}":":}}",}View DecoratorIf you're working withFlask,Bottleor a similar WSGI framework,qree.viewcan help bind route to templates.@app.route("/user-list")@qree.view("./views/user-list.html",variable="userList")defserve_userList():userList=yourLogicHere();returnuserList;The above is identical to the following:@app.route("/user-list")defserve_user_list_page():userList=yourLogicHere();returnqree.renderPath("./views/user-list.html",data=userList,variable="userList",);Vilo:UsingViloinstead of Flask/Bottle? Great choice! Qree jives with Vilo:@app.route("GET","/user-list")@qree.view("./views/user-list.html",variable="userList")defserve_userList(req,res):userList=yourLogicHere(req);returnuserList;Custom Tags:Like withqree.renderPath(.)andqree.renderStr(.), you can use custom tags withqree.view(.)by passingtagMap.Testing & ContributingInstall pytest viapip install -U pytest. Run tests with:pytestIf you encounter a bug, please open an issue on GitHub; but if you find a security vulnerability, please emailsecurity@polydojo.cominstead.If you'd like to see a new feature or contribute code, please open a GitHub issue. We'd love to hear from you! Suggestions and code contributions will always be appreciated, big and small.LicensingCopyright (c) 2020 Polydojo, Inc.Software Licensing:The software is released "AS IS" under theMIT license, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Kindly seeLICENSE.txtfor more details.No Trademark Rights:The above software licensing termsdo notgrant any right in the trademarks, service marks, brand names or logos of Polydojo, Inc.