Utilities
Tet provides a small set of focused utility modules under tet.util to
handle common tasks in web applications.
Cryptographic Utilities
The tet.util.crypt module provides password hashing built on top of
passlib’s SHA-256 crypt scheme.
Password Hashing
Two functions are exposed: crypt to hash a password and verify to
check a plaintext password against an existing hash. Both accept either
str or bytes for the password.
from tet.util.crypt import crypt, verify
# Hash a password
hashed = crypt("my_secret_password")
# Verify a password
if verify("my_secret_password", hashed):
print("Password is correct!")
Hashing uses passlib.hash.sha256_crypt (exposed as the module-level
password_hash), which handles salting automatically. For SQLAlchemy
models, consider tet.sqlalchemy.password.UserPasswordMixin, which
integrates this functionality directly into your model.
Base64 and Crockford Base32 Utilities
The tet.util.base64 module provides two codec classes, Base64 and
CrockfordBase32, both deriving from BaseCodec. Each codec exposes
encode, decode and normalize classmethods, plus a
generate_characters classmethod inherited from BaseCodec.
Standard Base64
from tet.util.base64 import Base64
encoded = Base64.encode(b"hello") # returns bytes
decoded = Base64.decode(encoded) # returns b"hello"
Base64.encode wraps base64.b64encode() and returns bytes;
Base64.decode wraps base64.b64decode(). Base64.normalize is a
no-op that returns its argument unchanged. The class attributes are
Base64.chars (a str of the 64-character alphabet,
string.ascii_letters + string.digits + "+/"), bits_per_char = 6 and
padding = True.
Crockford Base32
Crockford’s Base32 is a human-friendly encoding that avoids ambiguous
characters (0/O and 1/I/L). It is case-insensitive on
decode and tolerates common transcription mistakes.
from tet.util.base64 import CrockfordBase32
encoded = CrockfordBase32.encode(b"hello") # returns str
decoded = CrockfordBase32.decode(encoded) # returns bytes
# Ambiguous characters are normalized: O -> 0, I/L -> 1
CrockfordBase32.normalize("O1L") # "011"
CrockfordBase32.encode accepts str or bytes and returns a str
with any = padding stripped. CrockfordBase32.decode normalizes its
input by default (pass normalize=False to skip that), re-adds the
mandatory padding, and returns bytes. CrockfordBase32.normalize
translates the ambiguous characters and upper-cases the input. The class
attributes are CrockfordBase32.chars
("0123456789ABCDEFGHJKMNPQRSTVWXYZ", a str), bits_per_char = 5
and padding = False.
Generating Random Characters
BaseCodec.generate_characters produces a random string of the requested
length using the codec’s own alphabet, drawn from a cryptographically secure
source:
from tet.util.base64 import Base64, CrockfordBase32
# 16 random Base64 characters
token = Base64.generate_characters(16)
# 26 random Crockford Base32 characters (good for IDs / tokens)
ident = CrockfordBase32.generate_characters(26)
Internally it generates ceil(length * bits_per_char / 8) random bytes
with secrets.token_bytes(), runs them through the codec’s encode,
and truncates the result to length characters. Because any padding only
trails the data, the truncated slice never contains padding. A non-positive
length returns an empty string.
Collection Utilities
The tet.util.collections module provides a single helper, flatten.
Flattening Nested Iterables
flatten is a generator that recursively flattens an arbitrarily nested
iterable. str and bytes are treated as atomic values and are never
exploded into their characters.
from tet.util.collections import flatten
nested = [1, [2, 3, [4, 5]], 6]
list(flatten(nested)) # [1, 2, 3, 4, 5, 6]
with_strings = ["hello", ["world", ["!"]]]
list(flatten(with_strings)) # ["hello", "world", "!"]
Path Utilities
The tet.util.path module provides caller_package, used internally by
Tet’s configuration system to determine which package called into the
framework.
Determining the Calling Package
from tet.util.path import caller_package
# The package module of the code that called the current function
pkg = caller_package()
# Skip additional modules when walking the stack
pkg = caller_package(ignored_modules=("myframework.helpers",))
caller_package walks up the call stack (starting a few frames up, and
always ignoring tet.util.path itself), skipping any module whose name is
in ignored_modules. When it reaches the first non-ignored module it
returns that module if it is itself a package (its __file__ ends in
__init__.py), otherwise it returns the package that contains the module.
It builds on Pyramid’s pyramid.path.caller_module, which can also be
overridden via the caller_module keyword argument for testing.
Export Utilities
The tet.util.export module provides exporter, a small helper for
maintaining a module’s __all__ via a decorator.
Maintaining __all__
exporter() returns a (decorator, list) tuple. Bind the list to your
module’s __all__ and apply the decorator to anything you want exported;
each decorated object’s __name__ is appended to __all__ and the object
is returned unchanged.
from tet.util.export import exporter
export, __all__ = exporter()
@export
def my_public_function():
pass
@export
class MyPublicClass:
pass
def _private_function():
pass
# __all__ == ["my_public_function", "MyPublicClass"]
Shell (pshell) Utilities
The tet.util.pshell module provides snippet support for the Pyramid
pshell interactive environment. A snippet is a .py file that defines
a run() function, which can then be invoked interactively.
Configuring Snippets
Point the tet.snippets setting at a directory of snippet files in your INI
file:
[app:main]
tet.snippets = %(here)s/snippets
A snippet file snippets/create_user.py looks like:
def run(username, email):
from myapp.models import User
session = env["request"].dbsession
user = User(username=username, email=email)
session.add(user)
return user
Using Snippets in pshell
The Snippets factory builds a snippets-access object from an environment
mapping (the same env exposed in pshell). Each .py file in the
configured directory becomes an attribute that, when called, executes that
file’s run() function in the caller’s globals:
from tet.util.pshell import Snippets
snippets = Snippets(env)
# List available snippets
snippets()
# Invoke snippets/create_user.py's run() function
snippets.create_user("john", "john@example.com")
JSON Utilities
The tet.util.json module provides js_safe_dumps for serializing data
to JSON that is safe to embed directly inside an HTML <script> element.
Safe Embedding in HTML/JavaScript
js_safe_dumps serializes its argument with json.dumps() and then
escapes the characters that are dangerous in an HTML/JS context. The escaped
characters are <, >, / and & (XSS-prevention), as well as the
Unicode line/paragraph separators ``
`` and ``
`` (which would
otherwise break JavaScript string literals).
from tet.util.json import js_safe_dumps
data = {"name": "<script>alert('xss')</script>"}
js_safe_dumps(data)
# '{"name": "\\u003cscript\\u003ealert(\'xss\')\\u003c\\u002fscript\\u003e"}'
In a Tonnikala template, use $literal() so the already-escaped JSON is not
double-escaped:
<script>
var config = $literal(js_safe_dumps(config_data));
</script>
Best Practices
- Use the secure codecs for tokens
Prefer
CrockfordBase32.generate_characters/Base64.generate_charactersfor identifiers and tokens; they draw fromsecrets.- Hash passwords, never store them
Use
cryptandverify(orUserPasswordMixin) for password storage.- Escape JSON destined for HTML
Use
js_safe_dumpswhenever JSON is embedded inside a<script>tag.