PageRenderTime 55ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/docs/source/plugins.rst

https://bitbucket.org/dhellmann/virtualenvwrapper
ReStructuredText | 399 lines | 292 code | 107 blank | 0 comment | 0 complexity | 4d304a5d650bff38721ef5f61e9e6e08 MD5 | raw file
  1. .. _plugins:
  2. ===========================
  3. Extending Virtualenvwrapper
  4. ===========================
  5. Long experience with home-grown solutions for customizing a
  6. development environment has proven how valuable it can be to have the
  7. ability to automate common tasks and eliminate persistent annoyances.
  8. Carpenters build jigs, software developers write shell scripts.
  9. virtualenvwrapper continues the tradition of encouraging a craftsman
  10. to modify their tools to work the way they want, rather than the other
  11. way around.
  12. There are two ways to attach your code so that virtualenvwrapper will
  13. run it: End-users can use shell scripts or other programs for personal
  14. customization, e.g. automatically performing an action on every new
  15. virtualenv (see :ref:`scripts`). Extensions can also be
  16. implemented in Python by using `Setuptools entry points`_, making it
  17. possible to share common behaviors between systems and developers.
  18. Use the hooks provided to eliminate repetitive manual operations and
  19. streamline your development workflow. For example, set up the
  20. :ref:`plugins-pre_activate` and :ref:`plugins-post_activate` hooks to
  21. trigger an IDE to load a project file to reload files from the last
  22. editing session, manage time-tracking records, or start and stop
  23. development versions of an application server. Use the
  24. :ref:`plugins-initialize` hook to add entirely new commands and hooks
  25. to virtualenvwrapper. And the :ref:`plugins-pre_mkvirtualenv` and
  26. :ref:`plugins-post_mkvirtualenv` hooks give you an opportunity to
  27. install basic requirements into each new development environment,
  28. initialize a source code control repository, or otherwise set up a new
  29. project.
  30. Defining an Extension
  31. =====================
  32. .. note::
  33. Virtualenvwrapper is delivered with a plugin for creating and
  34. running the user customization scripts
  35. (:ref:`extensions-user_scripts`). The examples below are taken from
  36. the implementation of that plugin.
  37. Code Organization
  38. -----------------
  39. The Python package for ``virtualenvwrapper`` is a *namespace package*.
  40. That means multiple libraries can install code into the package, even
  41. if they are not distributed together or installed into the same
  42. directory. Extensions can (optionally) use the ``virtualenvwrapper``
  43. namespace by setting up their source tree like:
  44. * virtualenvwrapper/
  45. * __init__.py
  46. * user_scripts.py
  47. And placing the following code in ``__init__.py``::
  48. """virtualenvwrapper module
  49. """
  50. __import__('pkg_resources').declare_namespace(__name__)
  51. .. note::
  52. Extensions can be loaded from any package, so using the
  53. ``virtualenvwrapper`` namespace is not required.
  54. Extension API
  55. -------------
  56. After the package is established, the next step is to create a module
  57. to hold the extension code. For example,
  58. ``virtualenvwrapper/user_scripts.py``. The module should contain the
  59. actual extension entry points. Supporting code can be included, or
  60. imported from elsewhere using standard Python code organization
  61. techniques.
  62. The API is the same for every extension point. Each uses a Python
  63. function that takes a single argument, a list of strings passed to the
  64. hook loader on the command line.
  65. ::
  66. def function_name(args):
  67. # args is a list of strings passed to the hook loader
  68. The contents of the argument list are defined for each extension point
  69. below (see :ref:`plugins-extension-points`).
  70. Extension Invocation
  71. --------------------
  72. Direct Action
  73. ~~~~~~~~~~~~~
  74. Plugins can attach to each hook in two different ways. The default is
  75. to have a function run and do some work directly. For example, the
  76. ``initialize()`` function for the user scripts plugin creates default
  77. user scripts when ``virtualenvwrapper.sh`` is loaded.
  78. ::
  79. def initialize(args):
  80. for filename, comment in GLOBAL_HOOKS:
  81. make_hook(os.path.join('$WORKON_HOME', filename), comment)
  82. return
  83. .. _plugins-user-env:
  84. Modifying the User Environment
  85. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  86. There are cases where the extension needs to update the user's
  87. environment (e.g., changing the current working directory or setting
  88. environment variables). Modifications to the user environment must be
  89. made within the user's current shell, and cannot be run in a separate
  90. process. To have code run in the user's shell process, extensions can
  91. define hook functions to return the text of the shell statements to be
  92. executed. These *source* hooks are run after the regular hooks with
  93. the same name, and should not do any work of their own.
  94. The ``initialize_source()`` hook for the user scripts plugin looks for
  95. a global initialize script and causes it to be run in the current
  96. shell process.
  97. ::
  98. def initialize_source(args):
  99. return """
  100. #
  101. # Run user-provided scripts
  102. #
  103. [ -f "$WORKON_HOME/initialize" ] && source "$WORKON_HOME/initialize"
  104. """
  105. .. warning::
  106. Because the extension is modifying the user's working shell, care
  107. must be taken not to corrupt the environment by overwriting
  108. existing variable values unexpectedly. Avoid creating temporary
  109. variables where possible, and use unique names where variables
  110. cannot be avoided. Prefixing variables with the extension name is
  111. a good way to manage the namespace. For example, instead of
  112. ``temp_file`` use ``user_scripts_temp_file``. Use ``unset`` to
  113. release temporary variable names when they are no longer needed.
  114. .. warning::
  115. virtualenvwrapper works under several shells with slightly
  116. different syntax (bash, sh, zsh, ksh). Take this portability into
  117. account when defining source hooks. Sticking to the simplest
  118. possible syntax usually avoids problems, but there may be cases
  119. where examining the ``SHELL`` environment variable to generate
  120. different syntax for each case is the only way to achieve the
  121. desired result.
  122. Registering Entry Points
  123. ------------------------
  124. The functions defined in the plugin need to be registered as *entry
  125. points* in order for virtualenvwrapper's hook loader to find them.
  126. Entry points are configured in the ``setup.py`` (or ``setup.cfg`` when
  127. using pbr) for your package by mapping the entry point name to the
  128. function in the package that implements it.
  129. This partial copy of virtualenvwrapper's ``setup.cfg`` illustrates how
  130. the ``initialize()`` and ``initialize_source()`` entry points are
  131. configured.
  132. .. include:: ../../setup.cfg
  133. :literal:
  134. :start-after: [entry_points]
  135. The ``entry_points`` section maps the *group names* to lists of entry
  136. point specifiers. A different group name is defined by
  137. virtualenvwrapper for each extension point (see
  138. :ref:`plugins-extension-points`).
  139. The entry point specifiers are strings with the syntax ``name =
  140. package.module:function``. By convention, the *name* of each entry
  141. point is the plugin name, but that is not required (the names are not
  142. used).
  143. .. seealso::
  144. * `namespace packages <https://setuptools.readthedocs.io/en/latest/setuptools.html#namespace-packages>`__
  145. * `Extensible Applications and Frameworks <https://setuptools.readthedocs.io/en/latest/setuptools.html#extensible-applications-and-frameworks>`__
  146. The Hook Loader
  147. ---------------
  148. Extensions are run through a command line application implemented in
  149. ``virtualenvwrapper.hook_loader``. Because ``virtualenvwrapper.sh``
  150. is the primary caller and users do not typically need to run the app
  151. directly, no separate script is installed. Instead, to run the
  152. application, use the ``-m`` option to the interpreter::
  153. $ python -m virtualenvwrapper.hook_loader -h
  154. Usage: virtualenvwrapper.hook_loader [options] <hook> [<arguments>]
  155. Manage hooks for virtualenvwrapper
  156. Options:
  157. -h, --help show this help message and exit
  158. -s, --source Print the shell commands to be run in the current
  159. shell
  160. -l, --list Print a list of the plugins available for the given
  161. hook
  162. -v, --verbose Show more information on the console
  163. -q, --quiet Show less information on the console
  164. -n NAMES, --name=NAMES
  165. Only run the hook from the named plugin
  166. To run the extensions for the initialize hook::
  167. $ python -m virtualenvwrapper.hook_loader -v initialize
  168. To get the shell commands for the initialize hook::
  169. $ python -m virtualenvwrapper.hook_loader --source initialize
  170. In practice, rather than invoking the hook loader directly it is more
  171. convenient to use the shell function, ``virtualenvwrapper_run_hook``
  172. to run the hooks in both modes.::
  173. $ virtualenvwrapper_run_hook initialize
  174. All of the arguments given to shell function are passed directly to
  175. the hook loader.
  176. Logging
  177. -------
  178. The hook loader configures logging so that messages are written to
  179. ``$WORKON_HOME/hook.log``. Messages also may be written to stderr,
  180. depending on the verbosity flag. The default is for messages at *info*
  181. or higher levels to be written to stderr, and *debug* or higher to go to
  182. the log file. Using logging in this way provides a convenient
  183. mechanism for users to control the verbosity of extensions.
  184. To use logging from within your extension, simply instantiate a logger
  185. and call its ``info()``, ``debug()`` and other methods with the
  186. messages.
  187. ::
  188. import logging
  189. log = logging.getLogger(__name__)
  190. def pre_mkvirtualenv(args):
  191. log.debug('pre_mkvirtualenv %s', str(args))
  192. # ...
  193. .. seealso::
  194. * `Standard library documentation for logging <https://docs.python.org/library/logging.html>`__
  195. * `PyMOTW for logging <https://www.doughellmann.com/PyMOTW/logging/>`__
  196. .. _plugins-extension-points:
  197. Extension Points
  198. ================
  199. The extension point names for native plugins follow a naming
  200. convention with several parts:
  201. ``virtualenvwrapper.(pre|post)_<event>[_source]``. The *<event>* is
  202. the action taken by the user or virtualenvwrapper that triggers the
  203. extension. ``(pre|post)`` indicates whether to call the extension
  204. before or after the event. The suffix ``_source`` is added for
  205. extensions that return shell code instead of taking action directly
  206. (see :ref:`plugins-user-env`).
  207. .. _plugins-get_env_details:
  208. get_env_details
  209. ===============
  210. The ``virtualenvwrapper.get_env_details`` hooks are run when
  211. ``workon`` is run with no arguments and a list of the virtual
  212. environments is printed. The hook is run once for each environment,
  213. after the name is printed, and can be used to show additional
  214. information about that environment.
  215. .. _plugins-initialize:
  216. initialize
  217. ----------
  218. The ``virtualenvwrapper.initialize`` hooks are run each time
  219. ``virtualenvwrapper.sh`` is loaded into the user's environment. The
  220. initialize hook can be used to install templates for configuration
  221. files or otherwise prepare the system for proper plugin operation.
  222. .. _plugins-pre_mkvirtualenv:
  223. pre_mkvirtualenv
  224. ----------------
  225. The ``virtualenvwrapper.pre_mkvirtualenv`` hooks are run after the
  226. virtual environment is created, but before the new environment is
  227. activated. The current working directory for when the hook is run is
  228. ``$WORKON_HOME`` and the name of the new environment is passed as an
  229. argument.
  230. .. _plugins-post_mkvirtualenv:
  231. post_mkvirtualenv
  232. -----------------
  233. The ``virtualenvwrapper.post_mkvirtualenv`` hooks are run after a new
  234. virtual environment is created and activated. ``$VIRTUAL_ENV`` is set
  235. to point to the new environment.
  236. .. _plugins-pre_activate:
  237. pre_activate
  238. ------------
  239. The ``virtualenvwrapper.pre_activate`` hooks are run just before an
  240. environment is enabled. The environment name is passed as the first
  241. argument.
  242. .. _plugins-post_activate:
  243. post_activate
  244. -------------
  245. The ``virtualenvwrapper.post_activate`` hooks are run just after an
  246. environment is enabled. ``$VIRTUAL_ENV`` is set to point to the
  247. current environment.
  248. .. _plugins-pre_deactivate:
  249. pre_deactivate
  250. --------------
  251. The ``virtualenvwrapper.pre_deactivate`` hooks are run just before an
  252. environment is disabled. ``$VIRTUAL_ENV`` is set to point to the
  253. current environment.
  254. .. _plugins-post_deactivate:
  255. post_deactivate
  256. ---------------
  257. The ``virtualenvwrapper.post_deactivate`` hooks are run just after an
  258. environment is disabled. The name of the environment just deactivated
  259. is passed as the first argument.
  260. .. _plugins-pre_rmvirtualenv:
  261. pre_rmvirtualenv
  262. ----------------
  263. The ``virtualenvwrapper.pre_rmvirtualenv`` hooks are run just before
  264. an environment is deleted. The name of the environment being deleted
  265. is passed as the first argument.
  266. .. _plugins-post_rmvirtualenv:
  267. post_rmvirtualenv
  268. -----------------
  269. The ``virtualenvwrapper.post_rmvirtualenv`` hooks are run just after
  270. an environment is deleted. The name of the environment being deleted
  271. is passed as the first argument.
  272. Adding New Extension Points
  273. ===========================
  274. Plugins that define new operations can also define new extension
  275. points. No setup needs to be done to allow the hook loader to find
  276. the extensions; documenting the names and adding calls to
  277. ``virtualenvwrapper_run_hook`` is sufficient to cause them to be
  278. invoked.
  279. The hook loader assumes all extension point names start with
  280. ``virtualenvwrapper.`` and new plugins will want to use their own
  281. namespace qualifier to append to that. For example, the project_
  282. extension defines new events around creating project directories (pre
  283. and post). These are called
  284. ``virtualenvwrapper.project.pre_mkproject`` and
  285. ``virtualenvwrapper.project.post_mkproject``. These are invoked
  286. with::
  287. virtualenvwrapper_run_hook project.pre_mkproject $project_name
  288. and::
  289. virtualenvwrapper_run_hook project.post_mkproject
  290. respectively.
  291. .. _Setuptools entry points: https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points
  292. .. _project: https://bitbucket.org/virtualenvwrapper/virtualenvwrapper