API

These names are printed without their package name; some packages provide classes and methods that share a name, so please check the full name of any references to avoid confusion.

The Application

class junior.Application(name='app', **params)

Bases: flask.app.Flask

A wrapper around Flask, serving as the single persistent instance of our WSGI application. It lasts for the application’s entire lifecycle, until terminated by the user or server software.

Application accepts the same parameters as Flask.

Parameters

name – the name of our application; passed to import_name and defaults to 'app'.

assets

An Environment to compile our application’s client-side source files.

db

An alias of junior.db.db.

is_started

Has the application been started? Defaults to False and becomes True after we call start().

locale

A Babel to add internationalization and localization tools to our application.

mail

A Mail to allow our application to send mail using an SMTP server. Its connection details are populated from our application’s configuration.

migrations

An Alembic to help us create migrations for our database structure.

schemas

A Marshmallow to serialize data records to be served by our RESTful API.

start()

Start the application. start() may only be called once per Application; subsequent calls will raise a ValueError.

start():
  • attaches our context processor, error handler, and authentication hook;

  • initializes our extensions;

  • registers api, components, and web.

store

An alias of junior.cache.store.

templates

An alias of jinja_env.

The Configuration

class junior.config.Config(*args, **kwargs)

Bases: munch.Munch, flask.config.Config

A Config wrapped in X.

junior.config.babel = {'javascript': {'{components_path}/**.js': {'extract_messages': ['_', '__']}, '{scripts_path}/**.js': {'extract_messages': ['_', '__']}}, 'jinja2': {'{components_path}/**.haml': {'encoding': 'utf-8', 'extensions': ['webassets.ext.jinja2.AssetsExtension', 'hamlish_jinja.HamlishExtension'], 'variable_end_string': '{templates_expressions_close}', 'variable_start_string': '{templates_expressions_open}'}, '{templates_path}/**.haml': {'encoding': 'utf-8', 'extensions': ['webassets.ext.jinja2.AssetsExtension', 'hamlish_jinja.HamlishExtension'], 'variable_end_string': '{templates_expressions_close}', 'variable_start_string': '{templates_expressions_open}'}}}

Our Babel options.

junior.config.celery = {'broker_transport_options': {'data_folder_in': '.cache/queue', 'data_folder_out': '.cache/queue', 'data_folder_processed': '.cache'}, 'broker_url': 'filesystem://', 'result_backend': 'file://.cache'}

Our Celery options.

junior.config.config = {'api': {'updated_at': datetime.datetime(2020, 1, 1, 0, 0), 'version': 1}}

Our configuration options.

junior.config.defaults = {'alembic': {}, 'auth_factor': 10, 'cache_path': '.cache', 'cache_timeout': 60, 'cache_type': 'FileSystemCache', 'components_path': 'components', 'config_path': 'config', 'database_url': None, 'flask_debug': False, 'flask_env': 'production', 'migrations_path': 'migrations', 'proxy_count': 0, 'scripts_path': 'scripts', 'secret_key': None, 'sqlalchemy_track_modifications': False, 'static_path': 'static', 'static_timeout': 2592000, 'styles_path': 'styles', 'tasks_serializer': 'json', 'templates_expressions_close': '|}', 'templates_expressions_open': '{|', 'templates_path': 'templates'}

Our default env.

junior.config.env = {'alembic': {}, 'auth_factor': 10, 'cache_default_timeout': 60, 'cache_dir': '.cache', 'cache_path': '.cache', 'cache_timeout': 60, 'cache_type': 'FileSystemCache', 'components_path': 'components', 'config_path': 'config', 'database_revision': -1, 'database_url': None, 'flask_debug': False, 'flask_env': 'production', 'migrations_path': 'migrations', 'proxy_count': 0, 'scripts_path': 'scripts', 'secret_key': None, 'send_file_max_age_default': 2592000, 'sqlalchemy_database_uri': None, 'sqlalchemy_track_modifications': False, 'static_folder': 'static', 'static_path': 'static', 'static_timeout': 2592000, 'styles_path': 'styles', 'task_serializer': 'json', 'tasks_serializer': 'json', 'template_folder': 'templates', 'templates_expressions_close': '|}', 'templates_expressions_open': '{|', 'templates_path': 'templates'}

Our Application environment variables. env is passed to Application .config.

junior.config.jinja = {'extensions': ['hamlish_jinja.HamlishExtension'], 'variable_end_string': '|}', 'variable_start_string': '{|'}

Our Environment options.

junior.config.postcss = {'parser': 'sugarss', 'plugins': {'autoprefixer': {}, 'postcss-current-selector': {}, 'postcss-discard-comments': {'removeAll': True}, 'postcss-extend-rule': {}, 'postcss-font-magician': {'hosted': ['../fonts', 'fonts']}, 'postcss-functions': {}, 'postcss-mixins': {}, 'postcss-nested': {}, 'postcss-nested-ancestors': {}, 'postcss-nested-props': {}, 'postcss-preset-env': {}, 'postcss-property-lookup': {}, 'postcss-short': {}, 'postcss-sorting': {}, 'postcss-utilities': {}}}

Our PostCSS options.

junior.config.start(app)

Start our configuration service bound to app. start() wants to be called by start().

Parameters

app – an Application for us to configure.

junior.config.vendor = {}

Our vendor options.

The Database

junior.db.AlembicVersion = Table('alembic_version', MetaData(), Column('version_num', String(length=32), table=<alembic_version>, nullable=False), schema=None)

The alembic_version Table to store our migrations.

junior.db.db = <SQLAlchemy engine=None>

A SQLAlchemy to manage our application’s primary database connection. Its connection details are populated from our application’s configuration.

junior.db.filter(_event, attribute, function)

Apply a filter function to our results in response to an _event that targets attribute.

Parameters
  • _event – the Events we want to bind.

  • attribute – the attribute we want to watch.

  • function – the callback function we want to trigger.

junior.db.model(this)

Extend a new Model this with additional framework methods and properties.

We can use model() as a decorator on our new Model classes:

from junior import Model, model

@model
class User(Model):
    ...
Parameters

this – a Model to extend.

Adds

property this.Meta

Stores meta information about this.

property this.Meta.control

Describes the access controls for viewing, adding, updating, and deleting this records.

this.delete()

Delete this from the database.

this.fetch()

Fill this with values fetched from the database, using this.id as a key.

this.fill(**params)

Fill this with params.

this.save()

Commit this to the database.

junior.db.on(_event, attribute, function)

Trigger a function on an _event that targets attribute.

Parameters
  • _event – the Events we want to bind.

  • attribute – the attribute we want to watch.

  • function – the callback function we want to trigger.

junior.db.timestamps(model)

Add auto-managed created_at and updated_at fields to a model.

We can use timestamps() as a decorator on our new Model classes, before model():

from junior import Model, model, timestamps

@model
@timestamps
class User(Model):
    ...
Parameters

model – the sqlalchemy.event() we want to monitor.

The Controls

junior.controls.allow_all(model)

Allow any Resource we bind to model to select, update, and delete every record.

Parameters

model – a Model we want to bind to a Resource.

junior.controls.allow_belongs_to_me(model)

Allow any Resource we bind to model to select, update, and delete only records where user_id matches our current User id.

Parameters

model – a Model we want to bind to a Resource.

junior.controls.allow_self_for_me(model)

Allow any Resource we bind to model to select, update, and delete only records where id matches our current User id.

Parameters

model – a Model we want to bind to a Resource.

junior.controls.deny_all(model)

Deny any Resource we bind to model from selecting, updating, or deleting any records.

Parameters

model – a Model we want to bind to a Resource.

The User

class junior.auth.TokenResource

Bases: flask_restful.Resource

A Resource governing User authentication.

class Meta

Bases: object

TokenResource Meta.

endpoint = 'token'

The endpoint for TokenResource.

delete(**params)

Clear all existing tokens for an authenticated User.

get(**params)

Unused; returns [].

post(**params)

Authenticate a User by name and key, returning a token on success or raising Unauthorized on failure.

class junior.auth.User(**kwargs)

Bases: sqlalchemy.orm.decl_api.Model

A Model to help authenticate our users.

authenticate(key=None)

Authenticate a User from a key.

We can call authenticate() as either a static class method, or a local instance method, as our situation demands.

Called on a User instance, authenticate() accepts a plaintext key and compares it to our stored, hashed key, returning True if they match or False if they don’t.

Called directly on the User class, authenticate() accepts key as its first parameter and parses it as id and token, returning the selected User if they match or None if they don’t.

Parameters

key – a key or id/token pair matching the User we want to authenticate.

get_token()

Generate a new authentication token for this User.

id

The user’s primary key.

is_active

Can the user authenticate?

key

The user’s password.

name

The user’s name.

reset_token()

Clear all existing authentication tokens for this User.

resource

alias of junior.api.resource.<locals>.ModelResource

schema

alias of junior.api.resource.<locals>.ModelSchema

token

The user’s persistent token value.

junior.auth.authenticate()

Authenticate a User for this request. authenticate() uses the incoming Authorization HTTP header to assign me a User for the remainder of our Request lifecycle. authenticate() wants to be called by before_request().

junior.auth.me = None

The currently authenticated User. me is only valid within a single Request.

The REST

junior.api.api = <Blueprint 'api'>

A Blueprint governing the RESTful API.

junior.api.defaults()

Add a default set of routes to api:

  • / returns properties;

  • a missing path raises NotFound;

  • a raised Exception calls errorhandler() to return a JSON response with an appropriate error message and HTTP status code.

junior.api.properties = {'endpoints': {'Token': '/api/v1/token', 'User': '/api/v1/users/'}, 'root': '/api/v1/', 'updated_at': datetime.datetime(2020, 1, 1, 0, 0), 'version': 1}

The API’s properties. These values can be managed under the api section of config/app.yaml. properties.root defaults to '/api/v1/'; properties.version defaults to 1; properties.updated_at defaults to the date and time we import-ed junior.

junior.api.register(this, name=None, endpoint=None, *params)

Attach a Resource this to api. We can use register as a decorator on our new Model classes, after register:

from junior import Model, model, register, resource

@register
@resource
@model
class User(Model):
    ...
Parameters
  • this – the Resource we’ll attach to api.

  • name – the name of our Resource; defaults to the underlying Model’s pluralized name.

  • endpoint – the root URL path we’ll assign to our Resource; defaults to name transformed to snake_case.

junior.api.resource(this)

Create a new Resource from a Model this. We can use resource as a decorator on our new Model classes, after model:

from junior import Model, model, register, resource

@register
@resource
@model
class User(Model):
    ...

resource provides two classes:

  • ModelSchema is a SQLAlchemyAutoSchema bound to a Model. It helps us serialize a Model instance or collection into a JSON response.

  • ModelResource is a Resource bound to a Model. It provides us a set of RESTful routes that perform Model actions. The model’s Meta attribute controls the routes’ behaviour.

Parameters

this – the Model we’ll use to build a new Resource.

junior.api.resources = [<class 'junior.api.resource.<locals>.ModelResource'>, <class 'junior.auth.TokenResource'>]

The Resource objects we’ve attached to api.

junior.api.rest = <junior.api.Api object>

An Api bound to api. It allows us to attach Resource objects to api, assigning them each a URL based on their class name by default.

junior.api.schemas = <flask_marshmallow.Marshmallow object>

A Marshmallow to help us serialize database records into a plain text JSON response.

junior.api.start(app)

Start api and register it to app. start() wants to be called by start().

Parameters

app – an Application for us to register api.

The Assets

junior.assets.start(app)

Start the Environment we bound to app. start() wants to be called by start().

Parameters

app – an Application attached to an Environment.

The Index

junior.web.defaults()

Add a default set of routes to web:

  • / renders index.haml;

  • a “missing” path also renders index.haml and the user’s request path is sent to our client-side application;

  • /api/* redirects to api;

  • /favicon* redirects to our favicon asset path;

  • a raised Exception calls errorhandler() to render error.haml with an appropriate error message and HTTP status code.

junior.web.redirect_to_api(path, api_path='api')

Redirect the user to api if path matches api_path.

Parameters
  • path – the user’s requested URL path.

  • data – the root path prefix to api.

junior.web.start(app)

Start web and register it to app. start() wants to be called by start().

Parameters

app – an Application for us to register web.

junior.web.web = <Blueprint 'web'>

A Blueprint to serve the index template and redirect users to api where needed.

The Templates

junior.templates.templates = <jinja2.environment.Environment object>

An Environment to render our templates.

The Components

junior.components.components = <Blueprint 'components'>

A Blueprint serving our component templates.

junior.components.defaults()

Add a default set of routes to components:

  • the incoming path name is mapped to a component name and rendered;

  • a missing path raises returns a simple HTML comment response with HTTP status code 404;

  • a raised Exception calls errorhandler() to return an HTML comment response with an appropriate error message and HTTP status code.

junior.components.start(app)

Start components and register it to app. start() wants to be called by start().

Parameters

app – an Application for us to register components.

The Context

junior.context.asset(name)

Get the URL for an asset name.

Parameters

name – our asset’s filename.

junior.context.context = {'X': <function X>, '_': <function _>, 'asset': <function asset>, 'config': {'api': {'updated_at': datetime.datetime(2020, 1, 1, 0, 0), 'version': 1}}, 'dt': <class 'datetime.datetime'>, 'now': datetime.datetime(2021, 6, 5, 20, 28, 51, 984394), 'title': <function title>}

Our Application context, as exposed to our templates.

junior.context.process(key=None)

A wrapper around context.

Parameters

key – the key to fetch from context.

junior.context.title(text=None)

Append the site’s name to text to give us a page title. If text is None, title() returns the site’s name.

Parameters

text – our page’s title text.

The Cache

junior.cache.add(*args, **kwargs)

Add a new item to store. add is an alias of add.

Return type

bool

junior.cache.cache(timeout=None, key_prefix='view/%s', unless=None, forced_update=None, response_filter=None, query_string=False, hash_method=<built-in function openssl_md5>, cache_none=False, make_cache_key=None, source_check=None)

Add a function to store. We can use cache as a decorator. cache is an alias of cached.

Parameters
  • timeout (Optional[int]) –

  • key_prefix (str) –

  • unless (Optional[Callable]) –

  • forced_update (Optional[Callable]) –

  • response_filter (Optional[Callable]) –

  • query_string (bool) –

  • hash_method (Callable) –

  • cache_none (bool) –

  • make_cache_key (Optional[Callable]) –

  • source_check (Optional[bool]) –

Return type

Callable

junior.cache.clear()

Clear all items from store. clear is an alias of clear.

Return type

None

junior.cache.delete(*args, **kwargs)

Delete items from store. delete is an alias of delete.

Return type

bool

junior.cache.forget(f, *args, **kwargs)

Delete or clear memoized functions from store. forget is an alias of delete_memoized.

Return type

None

junior.cache.get(*args, **kwargs)

Get an item from store. get is an alias of get.

Return type

Optional[Union[str, markupsafe.Markup]]

junior.cache.memo(timeout=None, make_name=None, unless=None, forced_update=None, response_filter=None, hash_method=<built-in function openssl_md5>, cache_none=False, source_check=None, args_to_ignore=None)

Add a function call’s return value to store using its arguments as a key. We can use memo as a decorator. memo is an alias of memoize.

Parameters
  • timeout (Optional[int]) –

  • make_name (None) –

  • unless (None) –

  • forced_update (Optional[Callable]) –

  • response_filter (None) –

  • hash_method (Callable) –

  • cache_none (bool) –

  • source_check (Optional[bool]) –

  • args_to_ignore (Optional[Any]) –

Return type

Callable

junior.cache.set(*args, **kwargs)

Add an item to store. set is an alias of set.

Return type

bool

junior.cache.store = <flask_caching.Cache object>

A Cache to store data to the file system for later retrieval. store uses FileSystemCache as its default cache provider.

The Queue

junior.queue.queue = <Celery app>

A Celery to process our queued tasks.

junior.queue.start(app)

Start queue bound to app. start() wants to be called by start().

Parameters

app – an Application for us to attach.

The Mail

junior.mail.mail(*messages)

Send a collection of email Message messages all at once.

Parameters

messages – the collection of Message objects we want to send.

The Socket

junior.socket.socket = <flask_socketio.SocketIO object>

A SocketIO allowing websocket clients to connect for a persistent, realtime channel.

The Errors

junior.errors.debug()

Capture the current Exception debug information.

junior.errors.error(exception)

Collect the data we’ll use to construct a Response after an exception was raised.

Parameters

exception – the Exception that triggered a Response.

class junior.errors.handle(path)

Bases: object

A wrapper to help us assign an error handler for requests to path.

We can use handle as a decorator, after errorhandler():

from junior import handle, web

@handle('/')
@web.errorhandler(Exception)
def error(exception):
    ...
Parameters

path – the path we’ll attach to our error handler.

junior.errors.start(app)

Start our global error handler bound to app. start() wants to be called by start().

Parameters

app – an Application for us to attach.

The Helpers

junior.util.X(_dict={}, **params)

X is a Munch. X() is also a function that returns a new X from a dict or a set of parameters.

Munch is “a dictionary that supports attribute-style access”. X() offers us a few options:

user = X(id=1, name='sheila', email='s@mail.ca')
user                            # we can create a new X from parameters
    X{'email': 's@mail.ca', 'id': 1, 'name': 'sheila'}
user['id']           # we can index by the usual brace attribute syntax
    1
user.name                        # or dot syntax if the key is a string
    'sheila'

book = X({'title': 'thing explainer', 'author': 'randall monroe'})
book                  # we can also create a new X by wrapping any dict
    X{'author': 'randall monroe', 'title': 'thing explainer'}
len(book)            # just like a dict, an X's length is its key count
    2
book.pages                              # we didn't define this one yet
    None

Missing keys return None; X prefers to fail silently.

X also gives us the toTOML() and fromTOML() methods to help serialize to and deserialize from the TOML format. Their signatures and behaviour match the toJSON()/fromJSON() and toYAML()/fromYAML() methods inherited from Munch.

Parameters

_dict – a dict to X-ify

junior.util._(string, plural_string=None, count=1)

Mark a string for translation. _() is wrapper for lazy_gettext().

Parameters
  • string – the string we want to translate.

  • plural_string – the plural form alternative of string.

  • count – the number of subjects, for deciding if we should pluralize.

junior.util.b(value, length=8, order='big', encoding='utf-8')

Cast an int or str value to a byte sequence:

b(46290)
    b'\x00\x00\x00\x00\x00\x00\xb4\xd2'
b(46290, order='little')
    b'\xd2\xb4\x00\x00\x00\x00\x00\x00'
b(511, 2)
    b'\x01\xff'
b('nice')
    b'nice'
b('你好欢迎')
    b'\xe4\xbd\xa0\xe5\xa5\xbd\xe6\xac\xa2\xe8\xbf\x8e'
b('你好欢迎', encoding='ascii')
    # UnicodeEncodeError: 'ascii' codec can't encode characters
    # in position 0-3: ordinal not in range(128)
Parameters
  • value – the data we want to cast.

  • length – the length we’ll use to cast value from int.

  • order – the endian-ness we’ll use to cast value from int.

  • encoding – the encoding we’ll use to cast value from str.

junior.util.echo(*args, _print=True)

Display the internals of a set of args. We can use echo() to render a debug str dump of any object(s), printing it to the console by default.

Parameters

_print – print our result to the console?

junior.util.flatten(data, delimiter='_', reverse=False)

Flatten a nested dict or X data, joining its key names with delimiter:

flatten({'first':  {'one': 0, 'two': 1},
         'second': {'one': 2, 'two': 3}})
    {'first_one': 0, 'first_two': 1, 'second_one': 2, 'second_two': 3}

flatten(X(name=X(first='al', middle='bert', last='catt')), '.', True)
    X{'first.name': 'al', 'last.name': 'catt', 'middle.name': 'bert'}
Parameters
  • data – the dict or X we want to flatten.

  • delimiter – the str we’ll use to join our nested key names.

  • reverse – join our key names in reverse order?

The Shortcuts

We can import a number of commonly used tools directly from the junior package to help us consolidate our import statements.

Several names are provided by an alias. If the default import name is confusing or blocks one of our other names, we can import ... as nice_name.

junior.Blueprint

flask.Blueprint

junior.Flask

flask.Flask

junior.Message

flask_mailman.EmailMessage

junior.Model

flask_sqlalchemy.Model

junior.Path

pathlib.Path

juinor.Request

flask.Request

juinor.Resource

flask_restful.Resource

juinor.Response

flask.Response

junior.User

junior.auth.User

junior.X()

junior.util.X()

junior._()

junior.util._()

junior.api

junior.api.api

junior.b()

junior.util.b()

junior.cache()

junior.cache.cache()

junior.components

junior.components.components

junior.config

junior.config.config

junior.context

junior.context.context

junior.db

junior.db.db

junior.dt

datetime.datetime

junior.emit()

flask_socketio.emit()

junior.echo()

junior.util.echo()

junior.env

junior.config.env

junior.error()

junior.errors.error()

junior.filter()

junior.db.filter()

junior.flatten()

junior.util.flatten()

junior.flash()

flask.flash()

junior.forget()

junior.cache.forget()

junior.g

flask.g

junior.get()

junior.cache.get()

junior.join()

flask.safe_join()

junior.jsonify()

flask.json.jsonify()

junior.mail()

junior.mail.mail()

junior.memo()

junior.cache.memo()

junior.model()

junior.db.model()

junior.on()

junior.db.on()

junior.path()

os.path.realpath()

junior.queue()

junior.queues.queue()

junior.redirect()

flask.redirect()

junior.register()

junior.api.register()

junior.render()

flask.render_template()

junior.request

flask.request

junior.resource()

junior.api.resource()

junior.response()

flask.make_response()

junior.schemas

junior.api.schemas

junior.send()

flask_socketio.send()

junior.session

flask.session

junior.set()

junior.cache.set()

junior.socket

junior.sockets.socket

junior.split()

os.path.split

junior.store

junior.cache.store

junior.synonym()

sqlalchemy.orm.synonym()

junior.td

datetime.timedelta

junior.to()

flask.url_for()

junior.timestamps()

junior.db.timestamps()

junior.web

junior.web.web

junior.validates()

sqlalchemy.orm.validates()