About Pluggable Apps
-------------------------
tgext.pluggable permits to plug extensions and applications inside a TG projects
much like the Django Apps.
Installing
-------------------------------
tgext.pluggable can be installed both from pypi or from bitbucket::
pip install tgext.pluggable
should just work for most of the users
Plugging Apps
----------------------------
In your application *config/app_cfg.py* import **plug**::
from tgext.pluggable import plug
Then at the *end of the file* call plug for each pluggable
application you want to enable (package_name must be
already installed in your python environment)::
plug(base_config, 'package_name')
The plug function accepts various optional arguments, for
example if the plugged application exposes a controller
you can mount it in a different place specifying a different
**appid**::
plug(base_config, 'package_name', 'new_app_id')
If you want to mount the pluggable application in a subcontroller
you can used a dotted **appid**, like ``subcontroller.appid``.
Note that ``subcontroller`` must exist in RootController.
Other options include:
- plug_helpers (True/False) -> Enable helpers injection
- plug_models (True/False) -> Enable models plugging
- plug_controller (True/False) -> Mount pluggable app root controller
- plug_bootstrap (True/False) -> Enable websetup.bootstrap plugging
- plug_statics (True/False) -> Enable plugged app statics
- rename_tables (True/False) -> Rename pluggable tables by prepending appid.
Relations with Plugged Apps Models
--------------------------------------
There are cases when you might need to create a relationship or a foreign key
with a model which is defined by a pluggable application. As pluggable application
models are loaded after loading your application they are not available at the
time your app models are imported.
``tgext.pluggable`` provides some utilities to make easier to create relations
with models defined by pluggable applications.
The first step you might want to take is setting the ``global_models=True``
parameter to the ``plug`` call, this will make all the models declared by the
pluggable application available to you::
plug(base_config, 'package_name', global_models=True)
After the specified pluggable application is plugged, the models will be available
inside your code through the ``tgext.pluggable.app_model`` object.
Then you can create foreign keys to the desired model using the
``tgext.pluggable.LazyForeignKey`` class and declare relations using the lazy
version of ``sqlalchemy.orm.relation``::
from tgext.pluggable import app_model, LazyForeignKey
class AdditionalInfo(DeclarativeBase):
__tablename__ = 'sample_model'
uid = Column(Integer, primary_key=True)
data = Column(Unicode(255), nullable=False)
plugged_model_id = Column(Integer, LazyForeignKey(lambda:app_model.PluggedModel.uid))
plugged_model = relation(lambda: app_model.PluggedModel)
Partials
--------------------------
tgext.pluggables provides a bunch of utilities to work with partials.
Partials in tgext.pluggable can be declared as a function or TGController
subclass method that has an *@expose* decorator. Those partials can lately
be rendered with::
${h.call_partial('module:function_name', arg1='Something')}
In the case of a class method::
${h.call_partial('module.Class:method', arg1='Something')}
The quickstarted pluggable application provides an example partial::
from tg import expose
@expose('plugappname.templates.little_partial')
def something(name):
return dict(name=name)
which can be rendered using::
${h.call_partial('plugappname.partials:something', name='Partial')}
Replacing Templates
--------------------------
tgext.pluggable provides a function to replace templates.
This is useful when you want to override the template that an application
you plugged in is exposing. To override call **replace_template** inside
your application config::
from tgext.pluggable import replace_template
replace_template(base_config, 'myapp.templates.about', 'myapp.templates.index')
**replace_template** will work even with tgext.pluggable partials, but
won't work with templates rendered directly calling the **render** method.
Calls to replace_template must be performed before the application has started.
Patching Templates
----------------------------
tgext.pluggable provides a function to patch templates, the result
of a template rendering will be passed through a list of operations which will
make possible to alter the rendering result.
This behavior is much inspired by **Deliverance** http://pythonhosted.org/Deliverance
meant for much simpler use cases. The most common usage is for small changes to templates
of plugged applications. For advanced manipulations using `replace_template` is suggested
as it's both faster and easier to maintain.
Template patching is enabled by using the `load_template_patches` function::
from tgext.pluggable import load_template_patches
load_template_patches(base_config)
Supposing your project is inside a Python distribution named **myapp** this will
load the ``template_patches.xml`` file from the root of the distribution and will
apply all the specified patches.
To load template patches from a python module (or pluggable) use::
load_template_patches(base_config, 'plugname')
You can use previous expression even to load patches from your own application
in case the distribution automatic detection failed.
Template patching format is an xml file in the form of::
<patches>
<patch template="tgext.crud.templates.get_all">
<content selector="#crud_content > h1" template="myapp.templates.replacements.crud_title" />
<append selector="#crud_content > h1" template="myapp.templates.replacements.crud_subtitle" />
<prepend selector="#crud_content > h1" template="myapp.templates.replacements.crud_superscript" />
<replace selector="#crud_btn_new > .add_link" template="" />
</patch>
</patches>
Each action listed inside the patch will be performed whenever the specified template
is rendered, the template associated to the action will be used as the content of the templacement
and the same data available to the patched template will be available to the action template too.
Available actions are:
* `content` - replaces the content of tags identified by the selector
* `append` - appends after the tags identified by the selector
* `prepend` - prepends before the tags identified by the selector
* `replace` - replaces the tags identified bt the selector.
Creating Pluggable Apps
----------------------------
tgext.pluggable provides a **quickstart-pluggable** command
to create a new pluggable application::
$ gearbox quickstart-pluggable plugtest
Enter package name [plugtest]:
...
The quickstarted application will provide an example on how to use
models, helpers, bootstrap, controllers and statics.
In the previous example the pluggable application can be enabled
inside any TurboGears using::
plug(base_config, 'plugtest')
After enabling the *plugtest* application you should run
*gearbox setup-app* inside your TurboGears project
to create the sample model. Then you can access the sample
application page though *http://localhost:8080/plugtest*
The plugme Entry Point
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Pluggable applications are required to implement a **plugme(app_config, options)** entry
point which will be called when plugging the application.
The plugme action is called before TurboGears configuration has been loaded so that
it is possible to register more pluggables inside the plugme hook. This way a pluggable
can plug any dependency it requires just by calling tgext.pluggable.plug inside its own
*plugme* function.
Any options passed to the plug call will be available inside the options dictionary,
other parts of the pluggable applications like controllers, models and so on will be
imported after the call to plugme so that plugme can set any configuration options that
will drive the behavior of the other parts.
Keep in mind that as plugme is called before loading the TurboGears configuration if you
need to perform something based on any configuration file option you must register a *setup*
from the plugme call and perform them there.
Changing Static Files Behavior
+++++++++++++++++++++++++++++++++++
By default every pluggable application serves all the static files available inside
its public directory as they are. This is performed by a WSGI application which is
in charge of serving the static files. Since version 0.2.1 it is now possible to
replace this application or apply any WSGI middleware to it through the
``static_middlewares`` option.
For example you can enable SCSS inside your pluggable application by
defining a ``plugme`` function like::
from tgext.scss import SCSSMiddleware
def plugme(app_config, options):
return dict(appid='plugtest', global_helpers=False, static_middlewares=[SCSSMiddleware])
Accessing Application Models from Pluggable Apps
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When creating a pluggable application you might often need to
access to some models that have been declared inside the
target application where the pluggable app will be mounted.
The most common use case for this is referencing the User, Group and Permission
models. To do this tgext.pluggable provides an **app_model** object which
wraps the application model and is initialized before loading the pluggable app.
This makes possible to access target application models referencing
them as **app_model.User** or **app_model.Group** and so on.
While you can guess that the primary key for those models is known
(for the app_model.User object for example you might consider referencing
to it as app_model.User.user_id) it is best practice to call the **primary_key**
function provided by tgext.pluggable to get a reference to its column.
This way it is possibile to declare relations to models which are not
provided by your pluggable app::
from tgext.pluggable import app_model, primary_key
user_id = Column(Integer, ForeignKey(primary_key(app_model.User)))
user = relation(app_model.User)
Pluggable Relative Urls
----------------------------------
It is possible to generate an url relative to a pluggable mount point
using the **plug_url(pluggable, path, params=None, lazy=False)** this
function is also exposed inside the application helpers when a pluggable
is used. For example to generate an url relative to the *plugtest* pluggable
it is possible to call plug_url::
plug_url('plugtest', '/')
To perform redirects inside a pluggable app the **plug_redirect(pluggable, path, params=None)**
function is provided. This function exposes the same interface as *plug_url* but
performs a redirect much like tg.redirect.
Internationalization
-------------------------------------
tgext.pluggable provides some utilities for to manage text translations inside
pluggables. When ``tg.i18n.ugettext`` or ``tg.i18n.lazy_ugettext`` are used
they will lookup for translations inside the Application and when not available
will fallback to the translations provided by the pluggable itself.
Messages extration and catalog creation/update for the pluggable work as in TurboGears
using Babel.
Just run inside the pluggable application the ``python setup.py extract_messages``
, ``python setup.py init_catalog -l LANG`` and ``python setup.py compile_catalog``
commands to create a catalog for ``LANG``.
Just distribute the catalogs with your pluggable application to make them
available and translated in applications that use it.
Managing Migrations
-------------------------------------
It is possible to initialize a migrations repository for a pluggable application.
This makes possible to evolve the database at later times for each pluggable application using
the `alembic <http://alembic.readthedocs.org/en/latest/tutorial.html#create-a-migration-script>`_ migration
library for SQLAlchemy.
Create Migration Repository
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To be able to manage migrations the pluggable has to be initialized with a migration repository
to perform so, the author of the pluggable application has to run::
$ gearbox migrate-pluggable plugtest init
Then to create migration scripts run::
$ gearbox migrate-pluggable plugtest create 'Add column for user_name'
A file named like `2c8c79324a5e_Add_column_for_user_name.py` will be available inside the `migration/versions` directory
of the pluggable application.
*Remember to add this directory to your distribution package to make it available to users of your pluggable application*
Using Migrations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the pluggable application your are using supports migrations it is possible to apply them
using the `upgrade` and `downgrade` commands.
It is possible to run `upgrade` to move forward::
$ gearbox migrate-pluggable plugtest upgrade
22:11:28,029 INFO [alembic.migration] Running upgrade None -> 3ca22a16fdcc
Or `downgrade` to revert a migration::
$ gearbox migrate-pluggable plugtest downgrade
22:15:24,004 INFO [alembic.migration] Running downgrade 3ca22a16fdcc -> None
The versioning commands support being called on all the pluggables enabled inside your application
by specifying `all` as the pluggable name. This will load your application to detect the plugged
apps and will run the specified command for each one of them::
$ gearbox migrate-pluggable all db_version
22:15:54,104 INFO [tgext.pluggable] Plugging plug1
22:15:54,105 INFO [tgext.pluggable] Plugging plug2
22:15:54,106 INFO [tgext.pluggable] Plugging plug3
Migrating plug1, plug3, plug2
plug1 Migrations
Repository '/tmp/PLUGS/plug1/migration'
Configuration File 'development.ini'
Versioning Table 'plug1_migrate'
22:15:54,249 INFO [alembic.migration] Context impl SQLiteImpl.
22:15:54,249 INFO [alembic.migration] Will assume transactional DDL.
Current revision for sqlite:////tmp/provaapp/devdata.db: 4edef05cc346 -> 1ae930148d69 (head), fourth migration
plug3 Migrations
Repository '/tmp/PLUGS/plug3/migration'
Configuration File 'development.ini'
Versioning Table 'plug3_migrate'
22:15:54,253 INFO [alembic.migration] Context impl SQLiteImpl.
22:15:54,254 INFO [alembic.migration] Will assume transactional DDL.
Current revision for sqlite:////tmp/provaapp/devdata.db: 15819683bb72 -> 453f571f41e4 (head), test migration
plug2 Migrations
Repository '/tmp/PLUGS/plug2/migration'
Configuration File 'development.ini'
Versioning Table 'plug2_migrate'
22:15:54,258 INFO [alembic.migration] Context impl SQLiteImpl.
22:15:54,259 INFO [alembic.migration] Will assume transactional DDL.
Current revision for sqlite:////tmp/provaapp/devdata.db: 154b4f69cbd1 -> 2c8c79324a5e (head), third migration
Raw data
{
"_id": null,
"home_page": "https://github.com/TurboGears/tgext.pluggable",
"name": "tgext.pluggable",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "turbogears2.extension",
"author": "Alessandro Molina, Jaroslav Mikul\u00edk",
"author_email": "alessandro.molina@axant.it, byczech@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/43/0f/54521835104bfb55309dedfe279b643867a43e9cf7edc56418b30e0a2bf8/tgext_pluggable-0.8.5.tar.gz",
"platform": null,
"description": "About Pluggable Apps\n-------------------------\n\ntgext.pluggable permits to plug extensions and applications inside a TG projects\nmuch like the Django Apps.\n\nInstalling\n-------------------------------\n\ntgext.pluggable can be installed both from pypi or from bitbucket::\n\n pip install tgext.pluggable\n\nshould just work for most of the users\n\nPlugging Apps\n----------------------------\n\nIn your application *config/app_cfg.py* import **plug**::\n\n from tgext.pluggable import plug\n\nThen at the *end of the file* call plug for each pluggable\napplication you want to enable (package_name must be\nalready installed in your python environment)::\n\n plug(base_config, 'package_name')\n\nThe plug function accepts various optional arguments, for\nexample if the plugged application exposes a controller\nyou can mount it in a different place specifying a different\n**appid**::\n\n plug(base_config, 'package_name', 'new_app_id')\n\nIf you want to mount the pluggable application in a subcontroller\nyou can used a dotted **appid**, like ``subcontroller.appid``.\nNote that ``subcontroller`` must exist in RootController.\n\n\nOther options include:\n\n - plug_helpers (True/False) -> Enable helpers injection\n - plug_models (True/False) -> Enable models plugging\n - plug_controller (True/False) -> Mount pluggable app root controller\n - plug_bootstrap (True/False) -> Enable websetup.bootstrap plugging\n - plug_statics (True/False) -> Enable plugged app statics\n - rename_tables (True/False) -> Rename pluggable tables by prepending appid.\n\nRelations with Plugged Apps Models\n--------------------------------------\n\nThere are cases when you might need to create a relationship or a foreign key\nwith a model which is defined by a pluggable application. As pluggable application\nmodels are loaded after loading your application they are not available at the\ntime your app models are imported.\n\n``tgext.pluggable`` provides some utilities to make easier to create relations\nwith models defined by pluggable applications.\n\nThe first step you might want to take is setting the ``global_models=True``\nparameter to the ``plug`` call, this will make all the models declared by the\npluggable application available to you::\n\n plug(base_config, 'package_name', global_models=True)\n\nAfter the specified pluggable application is plugged, the models will be available\ninside your code through the ``tgext.pluggable.app_model`` object.\n\nThen you can create foreign keys to the desired model using the\n``tgext.pluggable.LazyForeignKey`` class and declare relations using the lazy\nversion of ``sqlalchemy.orm.relation``::\n\n from tgext.pluggable import app_model, LazyForeignKey\n\n class AdditionalInfo(DeclarativeBase):\n __tablename__ = 'sample_model'\n\n uid = Column(Integer, primary_key=True)\n data = Column(Unicode(255), nullable=False)\n\n plugged_model_id = Column(Integer, LazyForeignKey(lambda:app_model.PluggedModel.uid))\n plugged_model = relation(lambda: app_model.PluggedModel)\n\n\nPartials\n--------------------------\n\ntgext.pluggables provides a bunch of utilities to work with partials.\nPartials in tgext.pluggable can be declared as a function or TGController\nsubclass method that has an *@expose* decorator. Those partials can lately\nbe rendered with::\n\n ${h.call_partial('module:function_name', arg1='Something')}\n\nIn the case of a class method::\n\n ${h.call_partial('module.Class:method', arg1='Something')}\n\nThe quickstarted pluggable application provides an example partial::\n\n from tg import expose\n\n @expose('plugappname.templates.little_partial')\n def something(name):\n return dict(name=name)\n\nwhich can be rendered using::\n\n ${h.call_partial('plugappname.partials:something', name='Partial')}\n\nReplacing Templates\n--------------------------\n\ntgext.pluggable provides a function to replace templates.\nThis is useful when you want to override the template that an application\nyou plugged in is exposing. To override call **replace_template** inside\nyour application config::\n\n from tgext.pluggable import replace_template\n\n replace_template(base_config, 'myapp.templates.about', 'myapp.templates.index')\n\n**replace_template** will work even with tgext.pluggable partials, but\nwon't work with templates rendered directly calling the **render** method.\n\nCalls to replace_template must be performed before the application has started.\n\nPatching Templates\n----------------------------\n\ntgext.pluggable provides a function to patch templates, the result\nof a template rendering will be passed through a list of operations which will\nmake possible to alter the rendering result.\n\nThis behavior is much inspired by **Deliverance** http://pythonhosted.org/Deliverance\nmeant for much simpler use cases. The most common usage is for small changes to templates\nof plugged applications. For advanced manipulations using `replace_template` is suggested\nas it's both faster and easier to maintain.\n\nTemplate patching is enabled by using the `load_template_patches` function::\n\n from tgext.pluggable import load_template_patches\n\n load_template_patches(base_config)\n\nSupposing your project is inside a Python distribution named **myapp** this will\nload the ``template_patches.xml`` file from the root of the distribution and will\napply all the specified patches.\n\nTo load template patches from a python module (or pluggable) use::\n\n load_template_patches(base_config, 'plugname')\n\nYou can use previous expression even to load patches from your own application\nin case the distribution automatic detection failed.\n\nTemplate patching format is an xml file in the form of::\n\n <patches>\n <patch template=\"tgext.crud.templates.get_all\">\n <content selector=\"#crud_content > h1\" template=\"myapp.templates.replacements.crud_title\" />\n <append selector=\"#crud_content > h1\" template=\"myapp.templates.replacements.crud_subtitle\" />\n <prepend selector=\"#crud_content > h1\" template=\"myapp.templates.replacements.crud_superscript\" />\n <replace selector=\"#crud_btn_new > .add_link\" template=\"\" />\n </patch>\n </patches>\n\nEach action listed inside the patch will be performed whenever the specified template\nis rendered, the template associated to the action will be used as the content of the templacement\nand the same data available to the patched template will be available to the action template too.\nAvailable actions are:\n\n * `content` - replaces the content of tags identified by the selector\n\n * `append` - appends after the tags identified by the selector\n\n * `prepend` - prepends before the tags identified by the selector\n\n * `replace` - replaces the tags identified bt the selector.\n\nCreating Pluggable Apps\n----------------------------\n\ntgext.pluggable provides a **quickstart-pluggable** command\nto create a new pluggable application::\n\n $ gearbox quickstart-pluggable plugtest\n Enter package name [plugtest]:\n ...\n\nThe quickstarted application will provide an example on how to use\nmodels, helpers, bootstrap, controllers and statics.\n\nIn the previous example the pluggable application can be enabled\ninside any TurboGears using::\n\n plug(base_config, 'plugtest')\n\nAfter enabling the *plugtest* application you should run\n*gearbox setup-app* inside your TurboGears project\nto create the sample model. Then you can access the sample\napplication page though *http://localhost:8080/plugtest*\n\nThe plugme Entry Point\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPluggable applications are required to implement a **plugme(app_config, options)** entry\npoint which will be called when plugging the application.\n\nThe plugme action is called before TurboGears configuration has been loaded so that\nit is possible to register more pluggables inside the plugme hook. This way a pluggable\ncan plug any dependency it requires just by calling tgext.pluggable.plug inside its own\n*plugme* function.\n\nAny options passed to the plug call will be available inside the options dictionary,\nother parts of the pluggable applications like controllers, models and so on will be\nimported after the call to plugme so that plugme can set any configuration options that\nwill drive the behavior of the other parts.\n\nKeep in mind that as plugme is called before loading the TurboGears configuration if you\nneed to perform something based on any configuration file option you must register a *setup*\nfrom the plugme call and perform them there.\n\nChanging Static Files Behavior\n+++++++++++++++++++++++++++++++++++\n\nBy default every pluggable application serves all the static files available inside\nits public directory as they are. This is performed by a WSGI application which is\nin charge of serving the static files. Since version 0.2.1 it is now possible to\nreplace this application or apply any WSGI middleware to it through the\n``static_middlewares`` option.\n\nFor example you can enable SCSS inside your pluggable application by\ndefining a ``plugme`` function like::\n\n from tgext.scss import SCSSMiddleware\n\n def plugme(app_config, options):\n return dict(appid='plugtest', global_helpers=False, static_middlewares=[SCSSMiddleware])\n \nAccessing Application Models from Pluggable Apps\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWhen creating a pluggable application you might often need to\naccess to some models that have been declared inside the\ntarget application where the pluggable app will be mounted.\n\nThe most common use case for this is referencing the User, Group and Permission\nmodels. To do this tgext.pluggable provides an **app_model** object which\nwraps the application model and is initialized before loading the pluggable app.\n\nThis makes possible to access target application models referencing\nthem as **app_model.User** or **app_model.Group** and so on.\nWhile you can guess that the primary key for those models is known\n(for the app_model.User object for example you might consider referencing\nto it as app_model.User.user_id) it is best practice to call the **primary_key**\nfunction provided by tgext.pluggable to get a reference to its column.\n\nThis way it is possibile to declare relations to models which are not\nprovided by your pluggable app::\n\n from tgext.pluggable import app_model, primary_key\n\n user_id = Column(Integer, ForeignKey(primary_key(app_model.User)))\n user = relation(app_model.User)\n\nPluggable Relative Urls\n----------------------------------\n\nIt is possible to generate an url relative to a pluggable mount point\nusing the **plug_url(pluggable, path, params=None, lazy=False)** this\nfunction is also exposed inside the application helpers when a pluggable\nis used. For example to generate an url relative to the *plugtest* pluggable\nit is possible to call plug_url::\n\n plug_url('plugtest', '/')\n\nTo perform redirects inside a pluggable app the **plug_redirect(pluggable, path, params=None)**\nfunction is provided. This function exposes the same interface as *plug_url* but\nperforms a redirect much like tg.redirect.\n\nInternationalization\n-------------------------------------\n\ntgext.pluggable provides some utilities for to manage text translations inside\npluggables. When ``tg.i18n.ugettext`` or ``tg.i18n.lazy_ugettext`` are used\nthey will lookup for translations inside the Application and when not available\nwill fallback to the translations provided by the pluggable itself.\n \nMessages extration and catalog creation/update for the pluggable work as in TurboGears \nusing Babel. \nJust run inside the pluggable application the ``python setup.py extract_messages``\n, ``python setup.py init_catalog -l LANG`` and ``python setup.py compile_catalog``\ncommands to create a catalog for ``LANG``.\n\nJust distribute the catalogs with your pluggable application to make them\navailable and translated in applications that use it.\n\nManaging Migrations\n-------------------------------------\n\nIt is possible to initialize a migrations repository for a pluggable application.\nThis makes possible to evolve the database at later times for each pluggable application using\nthe `alembic <http://alembic.readthedocs.org/en/latest/tutorial.html#create-a-migration-script>`_ migration\nlibrary for SQLAlchemy.\n\nCreate Migration Repository\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTo be able to manage migrations the pluggable has to be initialized with a migration repository\nto perform so, the author of the pluggable application has to run::\n\n $ gearbox migrate-pluggable plugtest init\n\nThen to create migration scripts run::\n\n $ gearbox migrate-pluggable plugtest create 'Add column for user_name'\n\nA file named like `2c8c79324a5e_Add_column_for_user_name.py` will be available inside the `migration/versions` directory\nof the pluggable application.\n*Remember to add this directory to your distribution package to make it available to users of your pluggable application*\n\nUsing Migrations\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf the pluggable application your are using supports migrations it is possible to apply them\nusing the `upgrade` and `downgrade` commands.\n\nIt is possible to run `upgrade` to move forward::\n\n $ gearbox migrate-pluggable plugtest upgrade\n 22:11:28,029 INFO [alembic.migration] Running upgrade None -> 3ca22a16fdcc\n\nOr `downgrade` to revert a migration::\n\n $ gearbox migrate-pluggable plugtest downgrade\n 22:15:24,004 INFO [alembic.migration] Running downgrade 3ca22a16fdcc -> None\n\nThe versioning commands support being called on all the pluggables enabled inside your application\nby specifying `all` as the pluggable name. This will load your application to detect the plugged\napps and will run the specified command for each one of them::\n\n $ gearbox migrate-pluggable all db_version\n 22:15:54,104 INFO [tgext.pluggable] Plugging plug1\n 22:15:54,105 INFO [tgext.pluggable] Plugging plug2\n 22:15:54,106 INFO [tgext.pluggable] Plugging plug3\n Migrating plug1, plug3, plug2\n \n plug1 Migrations\n Repository '/tmp/PLUGS/plug1/migration'\n Configuration File 'development.ini'\n Versioning Table 'plug1_migrate'\n 22:15:54,249 INFO [alembic.migration] Context impl SQLiteImpl.\n 22:15:54,249 INFO [alembic.migration] Will assume transactional DDL.\n Current revision for sqlite:////tmp/provaapp/devdata.db: 4edef05cc346 -> 1ae930148d69 (head), fourth migration\n \n plug3 Migrations\n Repository '/tmp/PLUGS/plug3/migration'\n Configuration File 'development.ini'\n Versioning Table 'plug3_migrate'\n 22:15:54,253 INFO [alembic.migration] Context impl SQLiteImpl.\n 22:15:54,254 INFO [alembic.migration] Will assume transactional DDL.\n Current revision for sqlite:////tmp/provaapp/devdata.db: 15819683bb72 -> 453f571f41e4 (head), test migration\n \n plug2 Migrations\n Repository '/tmp/PLUGS/plug2/migration'\n Configuration File 'development.ini'\n Versioning Table 'plug2_migrate'\n 22:15:54,258 INFO [alembic.migration] Context impl SQLiteImpl.\n 22:15:54,259 INFO [alembic.migration] Will assume transactional DDL.\n Current revision for sqlite:////tmp/provaapp/devdata.db: 154b4f69cbd1 -> 2c8c79324a5e (head), third migration\n \n \n \n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Plug applications and extensions in a TurboGears2 project",
"version": "0.8.5",
"project_urls": {
"Homepage": "https://github.com/TurboGears/tgext.pluggable"
},
"split_keywords": [
"turbogears2.extension"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "4e26d49fad575c569cfd313809785cac6daac296bae38ec1cf96501c4dc36a7a",
"md5": "07f43c16d60fa4c196b63fb7f678df5c",
"sha256": "211d7635bc5bfdcef36f571d9147f8b2292007b02bed928bb2503efe6ee87f51"
},
"downloads": -1,
"filename": "tgext_pluggable-0.8.5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "07f43c16d60fa4c196b63fb7f678df5c",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 43079,
"upload_time": "2025-08-02T20:17:18",
"upload_time_iso_8601": "2025-08-02T20:17:18.022677Z",
"url": "https://files.pythonhosted.org/packages/4e/26/d49fad575c569cfd313809785cac6daac296bae38ec1cf96501c4dc36a7a/tgext_pluggable-0.8.5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "430f54521835104bfb55309dedfe279b643867a43e9cf7edc56418b30e0a2bf8",
"md5": "e96aafeb90f965ff27cef99a55379d7d",
"sha256": "cf3624b05811d25bee83fb66fd83848e0b297e52c4366228b2b69a1e2e162655"
},
"downloads": -1,
"filename": "tgext_pluggable-0.8.5.tar.gz",
"has_sig": false,
"md5_digest": "e96aafeb90f965ff27cef99a55379d7d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 35374,
"upload_time": "2025-08-02T20:17:19",
"upload_time_iso_8601": "2025-08-02T20:17:19.662941Z",
"url": "https://files.pythonhosted.org/packages/43/0f/54521835104bfb55309dedfe279b643867a43e9cf7edc56418b30e0a2bf8/tgext_pluggable-0.8.5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-02 20:17:19",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "TurboGears",
"github_project": "tgext.pluggable",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "tgext.pluggable"
}