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.FlaskA 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.Applicationaccepts the same parameters asFlask.- Parameters
name – the name of our application; passed to
import_nameand defaults to'app'.
- assets¶
An
Environmentto compile our application’s client-side source files.
- db¶
An alias of
junior.db.db.
- is_started¶
Has the application been started? Defaults to
Falseand becomesTrueafter we callstart().
- mail¶
A
Mailto allow our application to send mail using an SMTP server. Its connection details are populated from our application’s configuration.
- schemas¶
A
Marshmallowto serialize data records to be served by our RESTful API.
- start()¶
Start the application.
start()may only be called once perApplication; subsequent calls will raise a ValueError.start():attaches our context processor, error handler, and authentication hook;
initializes our extensions;
registers
api,components, andweb.
- store¶
An alias of
junior.cache.store.
The Configuration¶
- class junior.config.Config(*args, **kwargs)¶
Bases:
munch.Munch,flask.config.Config
- 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
Babeloptions.
- 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
Celeryoptions.
- 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
Applicationenvironment variables.envis passed toApplication.config.
- junior.config.jinja = {'extensions': ['hamlish_jinja.HamlishExtension'], 'variable_end_string': '|}', 'variable_start_string': '{|'}¶
Our
Environmentoptions.
- 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 bystart().- Parameters
app – an
Applicationfor 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_versionTableto store our migrations.
- junior.db.db = <SQLAlchemy engine=None>¶
A
SQLAlchemyto 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
functionto our results in response to an_eventthat targetsattribute.- Parameters
_event – the
Eventswe 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
Modelthiswith additional framework methods and properties.We can use
model()as a decorator on our newModelclasses:from junior import Model, model @model class User(Model): ...
- Parameters
this – a
Modelto extend.
Adds
- property this.Meta¶
Stores meta information about
this.
- property this.Meta.control¶
Describes the access controls for viewing, adding, updating, and deleting
thisrecords.
- this.delete()¶
Delete
thisfrom the database.
- this.fetch()¶
Fill
thiswith values fetched from the database, usingthis.idas a key.
- this.fill(**params)¶
Fill
thiswithparams.
- this.save()¶
Commit
thisto the database.
- junior.db.on(_event, attribute, function)¶
Trigger a
functionon an_eventthat targetsattribute.- Parameters
_event – the
Eventswe 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_atandupdated_atfields to amodel.We can use
timestamps()as a decorator on our newModelclasses, beforemodel():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
Resourcewe bind tomodelto select, update, and delete every record.
- junior.controls.allow_belongs_to_me(model)¶
Allow any
Resourcewe bind tomodelto select, update, and delete only records whereuser_idmatches our currentUserid.
The User¶
- class junior.auth.TokenResource¶
Bases:
flask_restful.ResourceA
ResourcegoverningUserauthentication.- class Meta¶
Bases:
objectTokenResourceMeta.- endpoint = 'token'¶
The endpoint for
TokenResource.
- get(**params)¶
Unused; returns
[].
- post(**params)¶
Authenticate a
Userbynameandkey, returning atokenon success or raisingUnauthorizedon failure.
- class junior.auth.User(**kwargs)¶
Bases:
sqlalchemy.orm.decl_api.ModelA
Modelto help authenticate our users.- authenticate(key=None)¶
Authenticate a
Userfrom akey.We can call
authenticate()as either a static class method, or a local instance method, as our situation demands.Called on a
Userinstance,authenticate()accepts a plaintextkeyand compares it to our stored, hashedkey, returningTrueif they match orFalseif they don’t.Called directly on the
Userclass,authenticate()acceptskeyas its first parameter and parses it asidandtoken, returning the selectedUserif they match orNoneif they don’t.
- id¶
The user’s primary key.
- is_active¶
Can the user authenticate?
- key¶
The user’s password.
- name¶
The user’s name.
- 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
Userfor this request.authenticate()uses the incomingAuthorizationHTTP header to assignmeaUserfor the remainder of ourRequestlifecycle.authenticate()wants to be called bybefore_request().
The REST¶
- junior.api.defaults()¶
Add a default set of routes to
api:/returnsproperties;a missing path raises
NotFound;a raised
Exceptioncallserrorhandler()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
apisection ofconfig/app.yaml.properties.rootdefaults to'/api/v1/';properties.versiondefaults to1;properties.updated_atdefaults to the date and time weimport-ed junior.
- junior.api.register(this, name=None, endpoint=None, *params)¶
Attach a
Resourcethistoapi. We can useregisteras a decorator on our newModelclasses, afterregister:from junior import Model, model, register, resource @register @resource @model class User(Model): ...
- junior.api.resource(this)¶
Create a new
Resourcefrom aModelthis. We can useresourceas a decorator on our newModelclasses, aftermodel:from junior import Model, model, register, resource @register @resource @model class User(Model): ...
resourceprovides two classes:ModelSchemais aSQLAlchemyAutoSchemabound to aModel. It helps us serialize aModelinstance or collection into a JSON response.ModelResourceis aResourcebound to aModel. It provides us a set of RESTful routes that performModelactions. The model’sMetaattribute controls the routes’ behaviour.
- junior.api.resources = [<class 'junior.api.resource.<locals>.ModelResource'>, <class 'junior.auth.TokenResource'>]¶
- junior.api.rest = <junior.api.Api object>¶
An
Apibound toapi. It allows us to attachResourceobjects toapi, assigning them each a URL based on their class name by default.
- junior.api.schemas = <flask_marshmallow.Marshmallow object>¶
A
Marshmallowto help us serialize database records into a plain text JSON response.
The Assets¶
- junior.assets.start(app)¶
Start the
Environmentwe bound toapp.start()wants to be called bystart().- Parameters
app – an
Applicationattached to anEnvironment.
The Index¶
- junior.web.defaults()¶
Add a default set of routes to
web:/rendersindex.haml;a “missing” path also renders
index.hamland the user’s request path is sent to our client-side application;/api/*redirects toapi;/favicon*redirects to our favicon asset path;a raised
Exceptioncallserrorhandler()to rendererror.hamlwith an appropriate error message and HTTP status code.
- junior.web.redirect_to_api(path, api_path='api')¶
Redirect the user to
apiifpathmatchesapi_path.- Parameters
path – the user’s requested URL path.
data – the root path prefix to
api.
The Templates¶
- junior.templates.templates = <jinja2.environment.Environment object>¶
An
Environmentto render our templates.
The Components¶
- junior.components.components = <Blueprint 'components'>¶
A
Blueprintserving 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
Exceptioncallserrorhandler()to return an HTML comment response with an appropriate error message and HTTP status code.
- junior.components.start(app)¶
Start
componentsand register it toapp.start()wants to be called bystart().- Parameters
app – an
Applicationfor us to registercomponents.
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
Applicationcontext, as exposed to our templates.
The Cache¶
- junior.cache.add(*args, **kwargs)¶
Add a new item to
store.addis an alias ofadd.- 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 usecacheas a decorator.cacheis an alias ofcached.- 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.delete(*args, **kwargs)¶
Delete items from
store.deleteis an alias ofdelete.- Return type
bool
- junior.cache.forget(f, *args, **kwargs)¶
Delete or clear memoized functions from
store.forgetis an alias ofdelete_memoized.- Return type
None
- junior.cache.get(*args, **kwargs)¶
Get an item from
store.getis an alias ofget.- 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
storeusing its arguments as a key. We can usememoas a decorator.memois an alias ofmemoize.- 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.store = <flask_caching.Cache object>¶
A
Cacheto store data to the file system for later retrieval.storeusesFileSystemCacheas its default cache provider.
The Queue¶
- junior.queue.start(app)¶
Start
queuebound toapp.start()wants to be called bystart().- Parameters
app – an
Applicationfor us to attach.
The Mail¶
- junior.mail.mail(*messages)¶
Send a collection of email
Messagemessagesall at once.- Parameters
messages – the collection of
Messageobjects we want to send.
The Socket¶
The Errors¶
- junior.errors.debug()¶
Capture the current
Exceptiondebug information.
- junior.errors.error(exception)¶
Collect the data we’ll use to construct a
Responseafter anexceptionwas raised.- Parameters
exception – the
Exceptionthat triggered aResponse.
- class junior.errors.handle(path)¶
Bases:
objectA wrapper to help us assign an error handler for requests to
path.We can use
handleas a decorator, aftererrorhandler():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 bystart().- Parameters
app – an
Applicationfor us to attach.
The Helpers¶
- junior.util.X(_dict={}, **params)¶
Xis a Munch.X()is also a function that returns a newXfrom adictor a set of parameters.Munchis “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;Xprefers to fail silently.Xalso gives us thetoTOML()andfromTOML()methods to help serialize to and deserialize from the TOML format. Their signatures and behaviour match thetoJSON()/fromJSON()andtoYAML()/fromYAML()methods inherited fromMunch.- Parameters
_dict – a
dicttoX-ify
- junior.util._(string, plural_string=None, count=1)¶
Mark a
stringfor translation._()is wrapper forlazy_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
intorstrvalueto 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
valuefromint.order – the endian-ness we’ll use to cast
valuefromint.encoding – the encoding we’ll use to cast
valuefromstr.
- junior.util.echo(*args, _print=True)¶
Display the internals of a set of
args. We can useecho()to render a debugstrdump 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
dictorXdata, joining its key names withdelimiter: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
dictorXwe want to flatten.delimiter – the
strwe’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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|