Metadata-Version: 2.1
Name: qree
Version: 0.0.2
Summary: Qree: Tiny but mighty Python templating.
Home-page: https://github.com/polydojo/qree
License: UNKNOWN
Author: Polydojo, Inc.
Author-email: 8079593+sumukhbarve@users.noreply.github.com
Description-Content-Type: text/markdown
Classifier: License :: OSI Approved :: MIT License

# Qree

Qree (read 'Curie') is a tiny but mighty Python templating engine, geared toward HTML. 'Qree' is short for: *Q*uote, *r*eplace, *e*xec(), *e*val().

Qree is a single module, under 100 lines of code. Instead of using regular expressions or PEGs, Qree relies on Python's `exec()` and `eval()`.  

**Warning:** As Qree uses `exec()`, it should **NOT** be used to render untrusted templates.

Qree supports all Python language features and is targeted toward Python 2.7+ and Python 3.6+.

**Quick Plug:** Qree built and maintained by the folks at [Polydojo, Inc.](https://www.polydojo.com/) If you're looking for a simple project management tool, please check out our latest product: [BoardBell.com](https://www.boardbell.com/).

## Installation:
```
pip install qree
```
Alternatively, just download `qree.py` into your project directory. Then, `import qree` as required.

## Text Interpolation:
Use `{{: expression :}}` for HTML-escaped interpolation, or `{{= expression =}}` for interpolation *without* escaping. The latter is susceptible to XSS, so please be careful. Here are a few quick examples:

**1. Hello, World!:**
```py
qree.renderStr("<h2>Hello, {{: data :}}", data="World!")
# Output: <h2>Hello, World!</h2>
```

**2. Using Expressions:**
```py
qree.renderStr("<h2>Mr. {{: data.title() + "!" :}}", data="bond")
# Output: <h2>Mr. Bond!</h2>
```

**3.  HTML Escaping:**
```py
qree.renderStr("<h2>Mr. {{: data :}}", data="<b> Villain </b> ")
# Output: <h2>Mr. &lt;b&gt; Villain &lt;/b&gt; </h2>
```

**4. Without Escaping:**
```py
qree.renderStr("<h2>Mr. {{= data =}}", data="<b> Villain </b> ")
# Output: <h2>Mr. <b> Villain </b> </h2>
```

**5. Longer Example:**
```py
qree.render("""
<!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 Code:
Any line beginning with `@=` is treated as Python code. (Preceding whitespace is ignored.)  You can write any python code you wish. You can define variables, import modules, etc. For example:

#### Leap Year Detection (with `lambda`):
```py
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 Indentation:

Python is an indented language. Use the special tags `@{` and `@}` for respectively indicating indentation and de-indentation to Qree. When used, such a tag should appear by itself on a separate line. For example:

#### Leap Year Detection (with `def`):
```py
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 Example
FizzBuzz 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.

```py
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
        Buzz
```

## The `data` variable:
By default, data passed via the `data` parameter is available in the template as the `data` variable. However, if you'd like to change the variable name, you may do so via the `variable` parameter. For example:
```py
qree.renderStr("Hi {{: name :}}!", data="Jim", variable="name")
# Output: Hi Jim!
```

## Template Files:
It's always convenient to store templates using separate files. To work with files, use `qree.renderPath(tplPath, data, ...)` instead of `qree.renderStr(tplStr, data, ...)`. 

#### Basic Example:

Let's say you have the following directory structure:
```
- app.py
- qree.py
- views/
        - homepage.html
```
Here's `homepage.html` :
```html
<doctype html>
<html>
<head><title>{{: data['title'] :}}</title></head>
<body>
    <h2>{{: data['title'] :}}</h2>
    <pre>{{: data['body'] :}}</pre>
</body>
<html>
```

In `app.py`, you could have the following snippet:
```py
def serve_homepage ():
    return qree.renderPath("./views/homepage.html", data={
        "title": "The TITLE Goes Here!",
        "body": "And the body goes here ...",
    });
```
Which would be equivalent to:
```py
def serve_homepage ():
    with open("./views/homepage.html", "r") as f:
        return qree.renderStr(f.read(), data={
            "title": "The TITLE Goes Here!",
            "body": "And the body goes here ...",
        });
```
In either case, the output would be:
```html
<doctype html>
<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>
```

## Nesting Templates:
Since templates can include any Python code, you can call `qree.renderPath()` from within a template! Consider the following directory structure:
```
- app.py
- qree.py
- views/
        - header.html
        - homepage.html
        - footer.html
```
With `header.html`:
```html
<header class="header">
    <a href="/link1">Link 1</a>
    <a href="/link2">Link 2</a>
    <a href="/link3">Link 3</a>
</header>
```

And similarly, `footer.html`:
```html
<footer class="footer">
    <a href="/linkA">Link A</a>
    <a href="/linkB">Link B</a>
    <a href="/linkC">Link C</a>
</footer>
```

Now, you can use `header.html` and `footer.html` in `homepage.html`:
```html
@= import qree;
<doctype html>
<html>
<head><title>{{: data['title'] :}}</title></head>
<body>
{{= qree.renderPath("./views/header.html", data=None) =}}
<h2>{{: data['title'] :}}</h2>
<pre>{{: data['body'] :}}</pre>
{{= qree.renderPath("./views/footer.html", data=None) =}}
</body>
<html>
```

And, as before, the snippet in `app.py`:
```py
def serve_homepage ():
    return qree.renderPath("./views/homepage.html", data={
        "title": "The TITLE Goes Here!",
        "body": "And the body goes here ...",
    });
```
Now, the output should be:
```html
<doctype html>
<html>
<head><title>The TITLE Goes Here!</title></head>
<body>
<header class="header">
    <a href="/link1">Link 1</a>
    <a href="/link2">Link 2</a>
    <a href="/link3">Link 3</a>
</header>
<h2>The TITLE Goes Here!</h2>
<pre>And the body goes here ...</pre>
<footer class="footer">
    <a href="/linkA">Link A</a>
    <a href="/linkB">Link B</a>
    <a href="/linkC">Link C</a>
</footer>
</body>
<html>
```

In the above example, we passed `data=None` to each nested template. But do realize that we could've passed anything. It's totally up to us. Additionally, as `data=None` is the default, we could've chosen to ignore the `data` parameter.


## Custom Tags (via `tagMap`)

Default tags like `{{:`, `:}}`, `@=`, etc. can each be customized via the `tagMap` parameter. Using `tagMap`, just supply your desired tag as the value against the default tag as key. A few examples follow:

#### 1. `[[:` Square `:]]` Brackets Instead Of `{{:` Braces `:}}`
```py
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  `@`)
```py
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.
```

### Default `tagMap`
The default values for each of the tags is as specified in the dict below.
```py
{   "@=": "@=",
    "@{": "@{",
    "@}": "@}",
    "{{=": "{{=", 
    "=}}": "=}}",
    "{{:": "{{:",
    ":}}": ":}}",
}
```

## View Decorator
If you're working with [Flask](https://flask.palletsprojects.com/), [Bottle](https://bottlepy.org/) or a similar WSGI framework, `qree.view` can help bind route to templates. 
```py
@app.route("/user-list")
@qree.view("./views/user-list.html", variable="userList")
def serve_userLsit ():
    userList = yourLogicHere();
    return userList;
```
The above is identical to the following:
```py
@app.route("/user-list")
@qree.view("./views/user-list.html" variable="userList")
def serve_user_list_page ():
    userList = yourLogicHere();
    return qree.renderPath("./views/user-list.html",
        data=userList, variable="userList",
    );
```
**Custom Tags:** Like with `qree.renderPath(.)`, you can use custom tags with `qree.view(.)` by supplying `tagMap`.

## License
Qree may be freely distributed under the MIT license. See [LICENSE.txt](https://github.com/polydojo/qree/blob/master/LICENSE.txt) for more details.

