Global options#
The GlobalOptions
class provides a generic mechanism for
setting and accessing global options for parents in one or several
related classes, typically for customizing the representation of their
elements. This class will eventually also support setting options on a
parent by parent basis.
These options should be “attached” to one or more classes as an options method.
See also
For good examples of GlobalOptions
in action see
sage.combinat.partition.Partitions.options
and
sage.combinat.tableau.Tableaux.options
.
Construction of options classes#
The general setup for creating a set of global options is:
sage: from sage.structure.global_options import GlobalOptions
sage: class MyOptions(GlobalOptions):
....: '''
....: Nice options
....:
....: @OPTIONS@
....: '''
....: NAME = 'option name'
....: module = 'sage.some_module.some_file'
....: option_class = 'name_of_class_controlled_by_options'
....: first_option = dict(default='with_bells',
....: description='Changes the functionality of _repr_',
....: values=dict(with_bells='causes _repr_ to print with bells',
....: with_whistles='causes _repr_ to print with whistles'),
....: alias=dict(bells='option1', whistles='option2'))
....: # second_option = dict(...)
....: # third_option = dict(...)
>>> from sage.all import *
>>> from sage.structure.global_options import GlobalOptions
>>> class MyOptions(GlobalOptions):
... '''
... Nice options
....:
>>> @OPTIONS@
... '''
... NAME = 'option name'
... module = 'sage.some_module.some_file'
... option_class = 'name_of_class_controlled_by_options'
... first_option = dict(default='with_bells',
... description='Changes the functionality of _repr_',
... values=dict(with_bells='causes _repr_ to print with bells',
... with_whistles='causes _repr_ to print with whistles'),
... alias=dict(bells='option1', whistles='option2'))
... # second_option = dict(...)
... # third_option = dict(...)
Note the syntax using the class
keyword. However, because of some
metaclass magic, the resulting MyOptions
object becomes an instance
of GlobalOptions
instead of a subclass. So, despite the class
syntax, MyOptions
is not a class.
The options constructed by GlobalOptions
have to be explicitly
associated to the class that they control using the following arguments:
NAME
– A descriptive name for the options class. This is optional; the default is the name of the constructed class.module
– The sage module containing the options class (optional)option_class
– The name of the options class. This is optional and defaults toNAME
if not explicitly set.
It is only possible to pickle a GlobalOptions
class if the
corresponding module is specified and if the options are explicitly
attached to the corresponding class as a options method.
Each option is specified as a dictionary which describes the possible values for the option and its documentation. The possible entries in this dictionary are:
alias
– Allows for several option values to do the same thing.alt_name
– An alternative name for this option.checker
– A validation function which returns whether a user supplied value is valid or not. This is typically useful for large lists of legal values such asNN
.default
– Gives the default value for the option.description
– A one line description of the option.link_to
– Links this option to another one in another set of global options. This is used for example to allowPartitions
andTableaux
to share the sameconvention
option.setter
– A function which is called after the value of the option is changed.values
– A dictionary assigning each valid value for the option to a short description of what it does.case_sensitive
– (Default:True
)True
orFalse
depending on whether the values of the option are case sensitive.
For each option, either a complete list of possible values, via values
, or a
validation function, via checker
, must be given. The values can be quite
arbitrary, including user-defined functions which customize the default
behaviour of the classes such as the output of _repr_
or latex()
. See
Dispatchers below, and _dispatcher()
, for more
information.
The documentation for the options is automatically constructed from
the docstring of the class by replacing the magic word @OPTIONS@
with a description of each option.
The basic structure for defining a GlobalOptions
class is best
illustrated by an example:
sage: from sage.structure.global_options import GlobalOptions
sage: class Menu():
....: class options(GlobalOptions):
....: '''
....: Fancy documentation
....: -------------------
....:
....: @OPTIONS@
....:
....: The END!
....: '''
....: NAME = 'menu'
....: entree = dict(default='soup',
....: description='The first course of a meal',
....: values=dict(soup='soup of the day', bread='oven baked'),
....: alias=dict(rye='bread'))
....: appetizer = dict(alt_name='entree')
....: main = dict(default='pizza', description='Main meal',
....: values=dict(pizza='thick crust', pasta='penne arrabiata'),
....: case_sensitive=False)
....: dessert = dict(default='espresso', description='Dessert',
....: values=dict(espresso='life begins again',
....: cake='waist begins again',
....: cream='fluffy, white stuff'))
....: tip = dict(default=10, description='Reward for good service',
....: checker = lambda tip: tip in range(0,20))
sage: Menu.options
Current options for menu
- dessert: espresso
- entree: soup
- main: pizza
- tip: 10
>>> from sage.all import *
>>> from sage.structure.global_options import GlobalOptions
>>> class Menu():
... class options(GlobalOptions):
... '''
... Fancy documentation
... -------------------
....:
>>> @OPTIONS@
....:
>>> The END!
... '''
... NAME = 'menu'
... entree = dict(default='soup',
... description='The first course of a meal',
... values=dict(soup='soup of the day', bread='oven baked'),
... alias=dict(rye='bread'))
... appetizer = dict(alt_name='entree')
... main = dict(default='pizza', description='Main meal',
... values=dict(pizza='thick crust', pasta='penne arrabiata'),
... case_sensitive=False)
... dessert = dict(default='espresso', description='Dessert',
... values=dict(espresso='life begins again',
... cake='waist begins again',
... cream='fluffy, white stuff'))
... tip = dict(default=10, description='Reward for good service',
... checker = lambda tip: tip in range(0,20))
>>> Menu.options
Current options for menu
- dessert: espresso
- entree: soup
- main: pizza
- tip: 10
In the examples above, the options are constructed when the options
object is created. However, it is also possible to construct the options
dynamically using the GlobalOptions._add_to_options()
methods.
For more details see GlobalOptions
.
Accessing and setting option values#
All options and their values, when they are strings, are forced to be lower case. The values of an options class can be set and accessed by calling the class or by treating the class as an array.
Continuing the example from Construction of options classes:
sage: Menu.options
Current options for menu
- dessert: espresso
- entree: soup
- main: pizza
- tip: 10
sage: Menu.options.dessert
espresso
sage: Menu.options.dessert = 'cake'
sage: Menu.options.dessert
cake
>>> from sage.all import *
>>> Menu.options
Current options for menu
- dessert: espresso
- entree: soup
- main: pizza
- tip: 10
>>> Menu.options.dessert
espresso
>>> Menu.options.dessert = 'cake'
>>> Menu.options.dessert
cake
Note that, provided there is no ambiguity, options and their values can be abbreviated:
sage: Menu.options('d')
'cake'
sage: Menu.options('m','t',des='esp', ent='sou') # get and set several values at once
['pizza', 10]
sage: Menu.options(t=15)
sage: Menu.options('tip')
15
sage: Menu.options.tip
15
sage: Menu.options(e='s', m='Pi'); Menu.options()
Current options for menu
- dessert: cake
- entree: soup
- main: pizza
- tip: 15
sage: Menu.options(m='P')
Traceback (most recent call last):
...
ValueError: P is not a valid value for main in the options for menu
>>> from sage.all import *
>>> Menu.options('d')
'cake'
>>> Menu.options('m','t',des='esp', ent='sou') # get and set several values at once
['pizza', 10]
>>> Menu.options(t=Integer(15))
>>> Menu.options('tip')
15
>>> Menu.options.tip
15
>>> Menu.options(e='s', m='Pi'); Menu.options()
Current options for menu
- dessert: cake
- entree: soup
- main: pizza
- tip: 15
>>> Menu.options(m='P')
Traceback (most recent call last):
...
ValueError: P is not a valid value for main in the options for menu
Setter functions#
Each option of a GlobalOptions
can be equipped with an optional setter
function which is called after the value of the option is changed. In the
following example, setting the option ‘add’ changes the state of the class by
setting an attribute in this class using a classmethod()
. Note that the
options object is inserted after the creation of the class in order to access
the classmethod()
as A.setter
:
sage: from sage.structure.global_options import GlobalOptions
sage: class A(SageObject):
....: state = 0
....: @classmethod
....: def setter(cls, option, val):
....: cls.state += int(val)
sage: class options(GlobalOptions):
....: NAME = "A"
....: add = dict(default=1,
....: checker=lambda v: int(v)>0,
....: description='An option with a setter',
....: setter=A.setter)
sage: A.options = options
sage: A.options
Current options for A
- add: 1
sage: a = A(); a.state
1
sage: a.options()
Current options for A
- add: 1
sage: a.options(add=4)
sage: a.state
5
sage: a.options()
Current options for A
- add: 4
>>> from sage.all import *
>>> from sage.structure.global_options import GlobalOptions
>>> class A(SageObject):
... state = Integer(0)
... @classmethod
... def setter(cls, option, val):
... cls.state += int(val)
>>> class options(GlobalOptions):
... NAME = "A"
... add = dict(default=Integer(1),
... checker=lambda v: int(v)>Integer(0),
... description='An option with a setter',
... setter=A.setter)
>>> A.options = options
>>> A.options
Current options for A
- add: 1
>>> a = A(); a.state
1
>>> a.options()
Current options for A
- add: 1
>>> a.options(add=Integer(4))
>>> a.state
5
>>> a.options()
Current options for A
- add: 4
Documentation for options#
The documentation for a GlobalOptions
is automatically generated from
the supplied options. For example, the generated documentation for the options
menu
defined in Construction of options classes is the following:
Fancy documentation
-------------------
OPTIONS:
- ``appetizer`` -- alternative name for ``entree``
- ``dessert`` -- (default: ``espresso``)
Dessert
- ``cake`` -- waist begins again
- ``cream`` -- fluffy, white stuff
- ``espresso`` -- life begins again
- ``entree`` -- (default: ``soup``)
The first course of a meal
- ``bread`` -- oven baked
- ``rye`` -- alias for ``bread``
- ``soup`` -- soup of the day
- ``main`` -- (default: ``pizza``)
Main meal
- ``pasta`` -- penne arrabiata
- ``pizza`` -- thick crust
- ``tip`` -- (default: ``10``)
Reward for good service
The END!
See :class:`~sage.structure.global_options.GlobalOptions` for more features of these options.
In addition, help on each option, and its list of possible values, can be obtained by (trying to) set the option equal to ‘?’:
sage: Menu.options.dessert? # not tested
- ``dessert`` -- (default: ``espresso``)
Dessert
- ``cake`` -- waist begins again
- ``cream`` -- fluffy, white stuff
- ``espresso`` -- life begins again
>>> from sage.all import *
>>> Menu.options.dessert? # not tested
- ``dessert`` -- (default: ``espresso``)
Dessert
- ``cake`` -- waist begins again
- ``cream`` -- fluffy, white stuff
- ``espresso`` -- life begins again
Dispatchers#
The whole idea of a GlobalOptions
class is that the options change the
default behaviour of the associated classes. This can be done either by simply
checking what the current value of the relevant option is. Another possibility
is to use the options class as a dispatcher to associated methods. To use the
dispatcher feature of a GlobalOptions
class it is necessary to implement
separate methods for each value of the option where the naming convention for
these methods is that they start with a common prefix and finish with the value
of the option.
If the value of a dispatchable option is set equal to a (user defined) function then this function is called instead of a class method.
For example, the options MyOptions
can be used to dispatch the _repr_
method of the associated class MyClass
as follows:
class MyClass(...):
def _repr_(self):
return self.options._dispatch(self,'_repr_','first_option')
def _repr_with_bells(self):
print('Bell!')
def _repr_with_whistles(self):
print('Whistles!')
class MyOptions(GlobalOptions):
...
In this example, first_option
is an option of MyOptions
which takes
values bells
, whistles
, and so on. Note that it is necessary to make
self
, which is an instance of MyClass
, an argument of the dispatcher
because _dispatch()
is a method of GlobalOptions
and not a method of MyClass
. Apart from MyOptions
, as it is a method of
this class, the arguments are the attached class (here MyClass
), the prefix
of the method of MyClass
being dispatched, the option of MyOptions
which controls the dispatching. All other arguments are passed through to the
corresponding methods of MyClass
. In general, a dispatcher is invoked as:
self.options._dispatch(self, dispatch_to, option, *args, **kargs)
Usually this will result in the method
dispatch_to + '_' + MyOptions(options)
of self
being called with
arguments *args
and **kargs
(if dispatch_to[-1] == '_'
then the
method dispatch_to + MyOptions(options)
is called).
If MyOptions(options)
is itself a function then the dispatcher will call
this function instead. In this way, it is possible to allow the user to
customise the default behaviour of this method. See
_dispatch()
for an example of how this can be achieved.
The dispatching capabilities of GlobalOptions
allows options to be
applied automatically without needing to parse different values of the option
(the cost is that there must be a method for each value). The dispatching
capabilities can also be used to make one option control several methods:
def __le__(self, other):
return self.options._dispatch(self, '_le_','cmp', other)
def __ge__(self, other):
return self.options._dispatch(self, '_ge_','cmp', other)
def _le_option_a(self, other):
return ...
def _ge_option_a(self, other):
return ...
def _le_option_b(self, other):
return ...
def _ge_option_b(self, other):
return ...
See _dispatch()
for more details.
Doc testing#
All of the options and their effects should be doc-tested. However, in order
not to break other tests, all options should be returned to their default state
at the end of each test. To make this easier, every GlobalOptions
class has
a _reset()
method for doing exactly this.
Pickling#
Options classes can only be pickled if they are the options for some standard
sage class. In this case the class is specified using the arguments to
GlobalOptions
. For example
options()
is defined as:
class Partitions(UniqueRepresentation, Parent):
...
class options(GlobalOptions):
NAME = 'Partitions'
module = 'sage.combinat.partition'
...
Here is an example to test the pickling of a GlobalOptions
instance:
sage: TestSuite(Partitions.options).run() # needs sage.combinat
>>> from sage.all import *
>>> TestSuite(Partitions.options).run() # needs sage.combinat
AUTHORS:
Andrew Mathas (2013): initial version
- Andrew Mathas (2016): overhaul making the options attributes, enabling
pickling and attaching the options to a class.
Jeroen Demeyer (2017): use subclassing to create instances
- class sage.structure.global_options.GlobalOptions(NAME=None, module='', option_class='', doc='', end_doc='', **options)[source]#
Bases:
object
The
GlobalOptions
class is a generic class for setting and accessing global options for Sage objects.While it is possible to create instances of
GlobalOptions
the usual way, the recommended syntax is to subclass fromGlobalOptions
. Thanks to some metaclass magic, this actually creates an instance ofGlobalOptions
instead of a subclass.INPUT (as “attributes” of the class):
NAME
– specifies a name for the options class (optional; default: class name)module
– gives the module that contains the associated options classoption_class
– gives the name of the associated module class (default:NAME
)option =
dict(...)
– dictionary specifying an option
The options are specified by keyword arguments with their values being a dictionary which describes the option. The allowed/expected keys in the dictionary are:
alias
– defines alias/synonym for option valuesalt_name
– alternative name for an optionchecker
– a function for checking whether a particular value for the option is validdefault
– the default value of the optiondescription
– documentation stringlink_to
– links to an option for this set of options to an option in anotherGlobalOptions
setter
– a function (class method) which is called whenever this option changesvalues
– a dictionary of the legal values for this option (this automatically defines the correspondingchecker
); this dictionary gives the possible options, as keys, together with a brief description of themcase_sensitive
– (default:True
)True
orFalse
depending on whether the values of the option are case sensitive
Options and their values can be abbreviated provided that this abbreviation is a prefix of a unique option.
EXAMPLES:
sage: from sage.structure.global_options import GlobalOptions sage: class Menu(): ....: class options(GlobalOptions): ....: ''' ....: Fancy documentation ....: ------------------- ....: ....: @OPTIONS@ ....: ....: End of Fancy documentation ....: ''' ....: NAME = 'menu' ....: entree = dict(default='soup', ....: description='The first course of a meal', ....: values=dict(soup='soup of the day', bread='oven baked'), ....: alias=dict(rye='bread')) ....: appetizer = dict(alt_name='entree') ....: main = dict(default='pizza', description='Main meal', ....: values=dict(pizza='thick crust', pasta='penne arrabiata'), ....: case_sensitive=False) ....: dessert = dict(default='espresso', description='Dessert', ....: values=dict(espresso='life begins again', ....: cake='waist begins again', ....: cream='fluffy white stuff')) ....: tip = dict(default=10, description='Reward for good service', ....: checker=lambda tip: tip in range(0,20)) sage: Menu.options Current options for menu - dessert: espresso - entree: soup - main: pizza - tip: 10 sage: Menu.options(entree='s') # unambiguous abbreviations are allowed sage: Menu.options(t=15) sage: (Menu.options['tip'], Menu.options('t')) (15, 15) sage: Menu.options() Current options for menu - dessert: espresso - entree: soup - main: pizza - tip: 15 sage: Menu.options._reset(); Menu.options() Current options for menu - dessert: espresso - entree: soup - main: pizza - tip: 10 sage: Menu.options.tip=40 Traceback (most recent call last): ... ValueError: 40 is not a valid value for tip in the options for menu sage: Menu.options(m='p') # ambiguous abbreviations are not allowed Traceback (most recent call last): ... ValueError: p is not a valid value for main in the options for menu
>>> from sage.all import * >>> from sage.structure.global_options import GlobalOptions >>> class Menu(): ... class options(GlobalOptions): ... ''' ... Fancy documentation ... ------------------- ....: >>> @OPTIONS@ ....: >>> End of Fancy documentation ... ''' ... NAME = 'menu' ... entree = dict(default='soup', ... description='The first course of a meal', ... values=dict(soup='soup of the day', bread='oven baked'), ... alias=dict(rye='bread')) ... appetizer = dict(alt_name='entree') ... main = dict(default='pizza', description='Main meal', ... values=dict(pizza='thick crust', pasta='penne arrabiata'), ... case_sensitive=False) ... dessert = dict(default='espresso', description='Dessert', ... values=dict(espresso='life begins again', ... cake='waist begins again', ... cream='fluffy white stuff')) ... tip = dict(default=10, description='Reward for good service', ... checker=lambda tip: tip in range(0,20)) >>> Menu.options Current options for menu - dessert: espresso - entree: soup - main: pizza - tip: 10 >>> Menu.options(entree='s') # unambiguous abbreviations are allowed >>> Menu.options(t=Integer(15)) >>> (Menu.options['tip'], Menu.options('t')) (15, 15) >>> Menu.options() Current options for menu - dessert: espresso - entree: soup - main: pizza - tip: 15 >>> Menu.options._reset(); Menu.options() Current options for menu - dessert: espresso - entree: soup - main: pizza - tip: 10 >>> Menu.options.tip=Integer(40) Traceback (most recent call last): ... ValueError: 40 is not a valid value for tip in the options for menu >>> Menu.options(m='p') # ambiguous abbreviations are not allowed Traceback (most recent call last): ... ValueError: p is not a valid value for main in the options for menu
The documentation for the options class is automatically generated from the information which specifies the options:
Fancy documentation ------------------- OPTIONS: - dessert: (default: espresso) Dessert - ``cake`` -- waist begins again - ``cream`` -- fluffy white stuff - ``espresso`` -- life begins again - entree: (default: soup) The first course of a meal - ``bread`` -- oven baked - ``rye`` -- alias for bread - ``soup`` -- soup of the day - main: (default: pizza) Main meal - ``pasta`` -- penne arrabiata - ``pizza`` -- thick crust - tip: (default: 10) Reward for good service End of Fancy documentation See :class:`~sage.structure.global_options.GlobalOptions` for more features of these options.
The possible values for an individual option can be obtained by (trying to) set it equal to ‘?’:
sage: Menu.options(des='?') - ``dessert`` -- (default: ``espresso``) Dessert - ``cake`` -- waist begins again - ``cream`` -- fluffy white stuff - ``espresso`` -- life begins again Current value: espresso
>>> from sage.all import * >>> Menu.options(des='?') - ``dessert`` -- (default: ``espresso``) Dessert <BLANKLINE> - ``cake`` -- waist begins again - ``cream`` -- fluffy white stuff - ``espresso`` -- life begins again <BLANKLINE> Current value: espresso
- class sage.structure.global_options.GlobalOptionsMeta(name, bases, dict)[source]#
Bases:
type
Metaclass for
GlobalOptions
This class is itself an instance of
GlobalOptionsMetaMeta
, which implements the subclass magic.
- class sage.structure.global_options.Option(options, name)[source]#
Bases:
object
An option.
Each option for an options class is an instance of this class which implements the magic that allows the options to the attributes of the options class that can be looked up, set and called.
By way of example, this class implements the following functionality.
EXAMPLES:
sage: # needs sage.combinat sage: Partitions.options.display list sage: Partitions.options.display = 'compact' sage: Partitions.options.display('list') sage: Partitions.options._reset()
>>> from sage.all import * >>> # needs sage.combinat >>> Partitions.options.display list >>> Partitions.options.display = 'compact' >>> Partitions.options.display('list') >>> Partitions.options._reset()