Refactoring Enable and Kiva

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Refactoring Enable and Kiva

Peter Wang
Hi everyone,

Last week, John, Bryce, and I had a discussion about the package
structure for Kiva and Enable, and the gorpiness of the current
toolkit and backend selection processes.  We came up with a much
cleaner, Python approach to both of these, and it involves making some
structural changes to Kiva, and moving some subpackages around in
Enable.

John has already started making the changes, and these should make it
into the next version of Kiva and Enable in ETS.  It's important to
note that most users should be unaffected by this change: the only
change outside of the Enable project itself is in ETSConfig, where the
logic for parsing the value of the "ETS_TOOLKIT" variable is handled.

-Peter

================================================
Refactoring Enable's and Kiva's Package Structures
================================================

Overview
========

This document describes the current import and backend detection logic in
Kiva and Enable, discusses the problems with the current approach, and
lays out a new structure for both packages.


Current Implementation
======================

(This applies to Enable version 3.3.2, which was part of ETS 3.5.0.  These
were released on 10/15/2010, and were included in EPD 6.3.)

Both Enable and Kiva have automatic backend detection logic that looks at
a combination of environment variables and (in the case of Kiva) available
packages on the system to determine the backends from which certain key
symbols are loaded.  For Enable, these are the :class:`Window`
and :class:`GraphicsContextEnable` classes.  For Kiva, these are the
:class:`GraphicsContext` class, :class:`Canvas`, and
:class:`CompiledPath` classes
and the :meth:`font_metrics_provider` function.

Enable uses the :var:`ENABLE_TOOLKIT` environment variable, if
defined.  If it is
not defined, then the value of :var:`ETSConfig.toolkit` is used.  (The ETSConfig
project, in turn, determines this by try/except testing of imports, and
looking at the :var:`ETS_TOOLKIT` environment variable.)  The valid values are
currently "wx", "qt", "pyglet", or "null".

Kiva also has its own backend detection logic.  If the :var:`ETSConfig.toolkit`
variable is defined, then the corresponding toolkit for that backend is
imported.  Any import failure will cause a RuntimeError.  If this variable is
None, then Kiva checks the :var:`KIVA_WISHLIST` environment variable for a list
of acceptable/preferred backends to try.  If this environment variable is not
defined, then it uses a default list of backends to try ("wx qt4 gl image pdf
svg ps cairo").  The first backend to import successfully without raising an
exception will be used.


Current Problems
================

Historically, there has been a direct correspondence between the Enable toolkit
and a particular Kiva backend.  Therefore, the Kiva backend detection process
generally didn't really serve much purpose except to get in the way in the rare
instances when a developer wished to import a particular backend directly.
Inserting auto-import magic into a package's __init__.py offers a little bit
of syntactic sugar at a heavy cost.

This problem is now exacerbated because there are starting to be a number of
potential Kiva backends for each of the Enable toolkits, and users of
Enable may wish to choose this more explicitly for various
application-dependent reasons.  It is cumbersome to force users to set both
ENABLE_TOOLKIT and KIVA_WISHLIST, and to magically know the valid combinations
thereof.  It is also highly un-Pythonic, since symbols and backends should
be determined by the subpackage to be imported, and not via an out-of-band,
environment variable mechanism.

There is one use case for specifying the toolkit/backend in a configuration
variable, and that is when a third party library or tool needs to behave
differently based on what Enable or Kiva backend is in use.  To support this
use case, we need to still expose the toolkit/backend selection, but do so
in a way that is more Pythonic.


New Package Structures
======================

The :var:`ENABLE_TOOLKIT` and :var:`KIVA_WISHLIST` environment variables will
be removed, in favor of just using the :var:`ETS_TOOLKIT` environment variable.
This variable will be extended to support the syntax
"[wx/qt/pyglet].[agg,quartz,gl,qpainter]".  If :ets:`ETS_TOOLKIT` environment
variable is not in the extended syntax, then a default will be chosen for
the given toolkit:

   * WxPython on non-Mac platforms: agg
   * WxPython on Mac: quartz
   * Qt: agg
   * Pyglet: gl

The :class:`ETSConfig` class will need to grow a :var:`kiva_backend` variable
that indicates the currently selected Kiva backend.  It will need to
be dependent
on the :var:`enable_toolkit` variable.

Kiva will undergo the following changes:

   1. Kiva will no longer try to determine a backend to import its
core symbols from.
   2. Kiva's __init__.py, which currently implements the logic, will be empty.
   3. Kiva's :class:`Canvas` class will be removed, as we are not
aware of anyone using it.
   4. If the KIVA_WISHLIST environment variable is defined, a
DeprecationWarning will be issued.
   5. The various Kiva backends will get shortened names, e.g.
      :class:`enthought.kiva.agg` instead of
      :class:`enthought.kiva.backend_image`, and :class:`enthought.kiva.wx`
      instead of :class:`enthought.kiva.backend_wx`.  (In fact, once we move
      Kiva to a top-level package name, it will be even shorter:
      :class:`kiva.agg` and :class:`kiva.wx`.)

Enable will be restructured as follows:

   1. The backends will be shorted and renamed similarly to Kiva,
e.g. :class:`enable.wx` and :class:`enable.qt4`.
   2. Each of the Enable toolkits will have a :class:`BaseWindow`
class that subclases from :class:`enable.AbstractWindow`.
   3. Each of the Kiva backend options for a toolkit will have its
own module, e.g. enable/qt4/agg.py and enable/qt4/qpainter.py.  Each
of these will define its own :class:`Window` class which will subclass
the :class:`BaseWindow` for the toolkit, and will explicitly import
the appropriate Kiva :class:`GraphicsContext`.
   4. The enable/toolkit.py module will use :class:`ETSConfig` to
determine which of the various toolkits and backends to import and
expose as the top-level :class:`enable.Window` and
:class:`enable.EnableGraphicsContext` classes.


Implications for Existing Users
===============================

Existing users of Enable who don't explicitly import from
:var:`enable.backend_wx` or :var:`enable.backend_qt4` explicitly, or
who don't set the :var:`ENABLE_TOOLKIT` environment variable, will be
completely insulated from these changes.

Existing users of Kiva will need to discontinue their use of the
:var:`KIVA_WISHLIST` environment variable, and change their code to
directly import symbols from the appropriate sub-package of Kiva that
corresponds to their desired backend.  Existing users of Kiva who are
actually relying on the "wishlist"-style of attempting to import
various backends until a working one is found will need to implement
the try/except import loop on their own, as this behavior will no
longer be implemented in Kiva.
_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev
Reply | Threaded
Open this post in threaded view
|

Re: Refactoring Enable and Kiva

John Wiggins
The changes have been merged into trunk. Please take a look at them if you have a chance. There might be bugs that need to be fixed before next week's ETS release :)

As a reminder, here are the most important changes:
- The environment variables ENABLE_TOOLKIT and KIVA_WISHLIST are gone. You can now access the same functionality via ETS_TOOLKIT. If you set the ETS toolkit to 'qt4' or 'wx', you shouldn't notice any difference with prior behavior. If you want to select a specific Kiva backend, you just add it after the toolkit like this: 'qt4.image', 'wx.gl', or 'wx.quartz'.

- Mirroring the changes to environment variables, the ETSConfig class has been modified slightly. ETSConfig.enable_toolkit now produces a warning if you try to read or write it. If you want to know which Kiva backend is in use (for debugging purposes), you can look at ETSConfig.kiva_backend. Also, if you assign a toolkit and kiva backend combination using the new extended syntax, the toolkit will end up in ETSConfig.toolkit and the kiva backend will end up in ETSConfig.kiva_backend. For example:

from enthought.etsconfig.api import ETSConfig
ETSConfig.toolkit = 'qt4.qpainter'
print ETSConfig.toolkit + ', ' + ETSConfig.kiva_backend

Will print: "qt4, qpainter". This behavior maintains compatibility with code that checks the value of ETSConfig.toolkit.

- If you want to use pdf/ps/svg/image output on a headless machine, you can use the null toolkit. 'null.image', 'null.pdf', 'null.ps', null.svg', and 'null.cairo' are the available choices.

- Importing GraphicsContext, CompiledPath, or font_metrics_provider from enthought.kiva no longer works. You must import from the backend you wish to use. If you just want to use whatever is currently in use, there are modules in enable that return the correct classes: enthought.enable.kiva_graphics_context, enthought.enable.compiled_path, and enthought.enable.font_metrics_provider.

- If you weren't interacting directly with Kiva before, you shouldn't even notice these changes. At least that is the intent :)
---

- John

On Thu, Jan 6, 2011 at 4:53 PM, Peter Wang <[hidden email]> wrote:
Hi everyone,

Last week, John, Bryce, and I had a discussion about the package
structure for Kiva and Enable, and the gorpiness of the current
toolkit and backend selection processes.  We came up with a much
cleaner, Python approach to both of these, and it involves making some
structural changes to Kiva, and moving some subpackages around in
Enable.

John has already started making the changes, and these should make it
into the next version of Kiva and Enable in ETS.  It's important to
note that most users should be unaffected by this change: the only
change outside of the Enable project itself is in ETSConfig, where the
logic for parsing the value of the "ETS_TOOLKIT" variable is handled.

-Peter

================================================
Refactoring Enable's and Kiva's Package Structures
================================================

Overview
========

This document describes the current import and backend detection logic in
Kiva and Enable, discusses the problems with the current approach, and
lays out a new structure for both packages.


Current Implementation
======================

(This applies to Enable version 3.3.2, which was part of ETS 3.5.0.  These
were released on 10/15/2010, and were included in EPD 6.3.)

Both Enable and Kiva have automatic backend detection logic that looks at
a combination of environment variables and (in the case of Kiva) available
packages on the system to determine the backends from which certain key
symbols are loaded.  For Enable, these are the :class:`Window`
and :class:`GraphicsContextEnable` classes.  For Kiva, these are the
:class:`GraphicsContext` class, :class:`Canvas`, and
:class:`CompiledPath` classes
and the :meth:`font_metrics_provider` function.

Enable uses the :var:`ENABLE_TOOLKIT` environment variable, if
defined.  If it is
not defined, then the value of :var:`ETSConfig.toolkit` is used.  (The ETSConfig
project, in turn, determines this by try/except testing of imports, and
looking at the :var:`ETS_TOOLKIT` environment variable.)  The valid values are
currently "wx", "qt", "pyglet", or "null".

Kiva also has its own backend detection logic.  If the :var:`ETSConfig.toolkit`
variable is defined, then the corresponding toolkit for that backend is
imported.  Any import failure will cause a RuntimeError.  If this variable is
None, then Kiva checks the :var:`KIVA_WISHLIST` environment variable for a list
of acceptable/preferred backends to try.  If this environment variable is not
defined, then it uses a default list of backends to try ("wx qt4 gl image pdf
svg ps cairo").  The first backend to import successfully without raising an
exception will be used.


Current Problems
================

Historically, there has been a direct correspondence between the Enable toolkit
and a particular Kiva backend.  Therefore, the Kiva backend detection process
generally didn't really serve much purpose except to get in the way in the rare
instances when a developer wished to import a particular backend directly.
Inserting auto-import magic into a package's __init__.py offers a little bit
of syntactic sugar at a heavy cost.

This problem is now exacerbated because there are starting to be a number of
potential Kiva backends for each of the Enable toolkits, and users of
Enable may wish to choose this more explicitly for various
application-dependent reasons.  It is cumbersome to force users to set both
ENABLE_TOOLKIT and KIVA_WISHLIST, and to magically know the valid combinations
thereof.  It is also highly un-Pythonic, since symbols and backends should
be determined by the subpackage to be imported, and not via an out-of-band,
environment variable mechanism.

There is one use case for specifying the toolkit/backend in a configuration
variable, and that is when a third party library or tool needs to behave
differently based on what Enable or Kiva backend is in use.  To support this
use case, we need to still expose the toolkit/backend selection, but do so
in a way that is more Pythonic.


New Package Structures
======================

The :var:`ENABLE_TOOLKIT` and :var:`KIVA_WISHLIST` environment variables will
be removed, in favor of just using the :var:`ETS_TOOLKIT` environment variable.
This variable will be extended to support the syntax
"[wx/qt/pyglet].[agg,quartz,gl,qpainter]".  If :ets:`ETS_TOOLKIT` environment
variable is not in the extended syntax, then a default will be chosen for
the given toolkit:

   * WxPython on non-Mac platforms: agg
   * WxPython on Mac: quartz
   * Qt: agg
   * Pyglet: gl

The :class:`ETSConfig` class will need to grow a :var:`kiva_backend` variable
that indicates the currently selected Kiva backend.  It will need to
be dependent
on the :var:`enable_toolkit` variable.

Kiva will undergo the following changes:

   1. Kiva will no longer try to determine a backend to import its
core symbols from.
   2. Kiva's __init__.py, which currently implements the logic, will be empty.
   3. Kiva's :class:`Canvas` class will be removed, as we are not
aware of anyone using it.
   4. If the KIVA_WISHLIST environment variable is defined, a
DeprecationWarning will be issued.
   5. The various Kiva backends will get shortened names, e.g.
      :class:`enthought.kiva.agg` instead of
      :class:`enthought.kiva.backend_image`, and :class:`enthought.kiva.wx`
      instead of :class:`enthought.kiva.backend_wx`.  (In fact, once we move
      Kiva to a top-level package name, it will be even shorter:
      :class:`kiva.agg` and :class:`kiva.wx`.)

Enable will be restructured as follows:

   1. The backends will be shorted and renamed similarly to Kiva,
e.g. :class:`enable.wx` and :class:`enable.qt4`.
   2. Each of the Enable toolkits will have a :class:`BaseWindow`
class that subclases from :class:`enable.AbstractWindow`.
   3. Each of the Kiva backend options for a toolkit will have its
own module, e.g. enable/qt4/agg.py and enable/qt4/qpainter.py.  Each
of these will define its own :class:`Window` class which will subclass
the :class:`BaseWindow` for the toolkit, and will explicitly import
the appropriate Kiva :class:`GraphicsContext`.
   4. The enable/toolkit.py module will use :class:`ETSConfig` to
determine which of the various toolkits and backends to import and
expose as the top-level :class:`enable.Window` and
:class:`enable.EnableGraphicsContext` classes.


Implications for Existing Users
===============================

Existing users of Enable who don't explicitly import from
:var:`enable.backend_wx` or :var:`enable.backend_qt4` explicitly, or
who don't set the :var:`ENABLE_TOOLKIT` environment variable, will be
completely insulated from these changes.

Existing users of Kiva will need to discontinue their use of the
:var:`KIVA_WISHLIST` environment variable, and change their code to
directly import symbols from the appropriate sub-package of Kiva that
corresponds to their desired backend.  Existing users of Kiva who are
actually relying on the "wishlist"-style of attempting to import
various backends until a working one is found will need to implement
the try/except import loop on their own, as this behavior will no
longer be implemented in Kiva.
_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev


_______________________________________________
Enthought-Dev mailing list
[hidden email]
https://mail.enthought.com/mailman/listinfo/enthought-dev