sas.system.config package

Submodules

sas.system.config.config module

Configuration class - stores configuration information for SasView

If you’re looking to change a field in the config file, you should read this

Configs

Configs are a nightmare from the perspective of code maintainability. There are three main reasons for this:

  1. They have a tendency to accumulate junk because people dont realise that a config item is no longer needed

  2. It’s hard to trace the usages and types because values are loaded at runtime

  3. Maintaining synchrony between config files and config usages is difficult, as it is the users that have control over the config files.

The Config class here attempts to resolve some of these issues in a way that preserves as many of the current uses as possible. It is a compromise between SasView’s current methods, and more standard ways of handling configurations.

Brief Outline

The main Config class provides a definition of the variables allowed in a config file, along with their default values. This class is used to generate a schema that determines how config files are read. Only a few types of variable are allowed

  • bool

  • int

  • float

  • str

  • Homogeneous lists of the above, to 10 levels of depth

  • None and empty list (please try to avoid)

Other types will throw an error at runtime when the schema is created.

None types and empty lists have no type information at runtime, so the schema cannot check/coerce the type of config variables when they are loaded. It is best to avoid having these as default values if possible.

The presence of a config file is not necessary for the functioning of the config system, only for making changes that differ from the default values.

What Belongs in a Config

Things that do belong:

  1. Program settings that are configurable by users through the GUI

  2. Program settings that have no GUI editor, but that some advanced users might want to set manually with a text editor

  3. Settings that control developer tools, e.g. debug modes

  4. Very little else

Things that don’t belong, but were previously in the config:

  1. dynamic content, i.e. values that are modified programmatically, this includes variables that are defined in terms of other variables, but otherwise don’t change

  2. Paths to resources within sasview (use importlib.resources instead)

  3. Blocks of data that won’t be modified by the user and used primarily by single class - e.g. the text for a message

  4. Large blocks of text in general

Making Changes to the Config Class

As users have their own copy of the sasview configuration, deletions, name changes, default value changes, and variable type changes often constitute a breaking change from the perspective of version control. The users locally stored config will, in general, not be backwards compatible with the new config. Extreme caution should be exercised - when changing the config, don’t just think about the problem at hand, but about the future maintainability of SasView in general.

Adding to the Config class: Before adding a variable, think about whether it might more properly belong somewhere else, perhaps in the web or legal classes in the system package. Remember that config variables are accessed across the whole of sasview and that names need to be suitably descriptive.

Deleting from the Config class: Currently (02/09/2022) the consequences of providing an entry in a config file that is not properly reflected in the Config class is a error. To ease backward compatibility, it is possible to disable the errors for a deleted class member by adding their name to the _deleted_attributes list. The suggested deletion process would be

` [-]   self.my_variable = 10 [+]   self._deleted_attributes.append("my_variable") `

At major releases, additions to _deleted_attributes should be removed.

Other Design Decisions made for the Config Class

The Config class cannot be dynamically modified, this prevents the config from having fields that are unspecified in the base class, and makes it so that all usages of fields can be automatically tracked.

Subclassing of Config is also disabled for similar reasons.

I have opted not to use frozen dataclasses at this time because, as they currently work, the behaviour would make creating different configs difficult.

class sas.system.config.config.Config

Bases: ConfigBase

__doc__ = None
__init__()
__module__ = 'sas.system.config.config'

sas.system.config.config_meta module

class sas.system.config.config_meta.ConfigBase

Bases: object

Base class for Config, keep the definition of config variables and workings as separate as possible

__annotations__ = {}
__dict__ = mappingproxy({'__module__': 'sas.system.config.config_meta', '__doc__': ' Base class for Config, keep the definition of config variables and workings as separate as possible ', '__init__': <function ConfigBase.__init__>, 'defaults': <property object>, 'config_filename': <function ConfigBase.config_filename>, 'finalise': <function ConfigBase.finalise>, 'update': <function ConfigBase.update>, 'save': <function ConfigBase.save>, 'override_with_defaults': <function ConfigBase.override_with_defaults>, 'save_to_file_object': <function ConfigBase.save_to_file_object>, 'load': <function ConfigBase.load>, 'load_from_file_object': <function ConfigBase.load_from_file_object>, '_state_copy': <function ConfigBase._state_copy>, '_generate_schema': <function ConfigBase._generate_schema>, '__setattr__': <function ConfigBase.__setattr__>, 'validate': <function ConfigBase.validate>, '__dict__': <attribute '__dict__' of 'ConfigBase' objects>, '__weakref__': <attribute '__weakref__' of 'ConfigBase' objects>, '__annotations__': {'_schema': 'Dict[str, SchemaElement]', '_defaults': 'Dict[str, SchemaElement]', '_deleted_attributes': 'List[str]', '_bad_entries': 'Dict[str, Any]'}})
__doc__ = ' Base class for Config, keep the definition of config variables and workings as separate as possible '
__init__()
__module__ = 'sas.system.config.config_meta'
__setattr__(key, value)

Implement setattr(self, name, value).

__weakref__

list of weak references to the object

_generate_schema() Dict[str, SchemaElement]

Auto-generate schema for the current config class and validate config class

Note: there is an assumption here that the class of the value in the default config file is

_state_copy() Dict[str, Any]

Get a copy of all the data in the config

config_filename(create_if_nonexistent=False)

Filename for saving config items

property defaults

Expose the default values to allow resetting of defaults. No setter should ever be created for this!

finalise()

Call this at the end of the config to make this class ‘final’ and to set up the config file schema

load()
load_from_file_object(file)

Load config file

override_with_defaults()

Set the config entries to defaults, and prevent saving from happening

Added with the ability to disable for testing in mind

save()
save_to_file_object(file)

Save config file

Only changed and unknown variables will be included in the saved file

update(data: Dict[str, Any])

Set the fields of the config from a dictionary

validate(key, value)

Check whether a value conforms to the type in the schema

exception sas.system.config.config_meta.ConfigLocked(message)

Bases: Exception

__doc__ = None
__init__(message)
__module__ = 'sas.system.config.config_meta'
__weakref__

list of weak references to the object

class sas.system.config.config_meta.ConfigMeta(name, bases, classdict)

Bases: type

__annotations__ = {}
__doc__ = None
__module__ = 'sas.system.config.config_meta'
static __new__(mcs, name, bases, classdict)
exception sas.system.config.config_meta.MalformedFile(message)

Bases: Exception

__doc__ = None
__init__(message)
__module__ = 'sas.system.config.config_meta'
__weakref__

list of weak references to the object

sas.system.config.schema_elements module

exception sas.system.config.schema_elements.CoercionError(message)

Bases: Exception

Raised when we can’t make a variable conform to the schema

__doc__ = " Raised when we can't make a variable conform to the schema"
__init__(message)
__module__ = 'sas.system.config.schema_elements'
__weakref__

list of weak references to the object

class sas.system.config.schema_elements.SchemaBool

Bases: SchemaVariable

__abstractmethods__ = frozenset({})
__doc__ = None
__module__ = 'sas.system.config.schema_elements'
_abc_impl = <_abc._abc_data object>
coerce(value)

Force a variable to conform to the schema, if possible

schema_variable_type: str = 'bool'
class sas.system.config.schema_elements.SchemaElement

Bases: ABC

Base class for schema elements

__abstractmethods__ = frozenset({'coerce'})
__dict__ = mappingproxy({'__module__': 'sas.system.config.schema_elements', '__doc__': ' Base class for schema elements', 'coerce': <function SchemaElement.coerce>, 'validate': <function SchemaElement.validate>, '__eq__': <function SchemaElement.__eq__>, '__dict__': <attribute '__dict__' of 'SchemaElement' objects>, '__weakref__': <attribute '__weakref__' of 'SchemaElement' objects>, '__hash__': None, '__abstractmethods__': frozenset({'coerce'}), '_abc_impl': <_abc._abc_data object>, '__annotations__': {}})
__doc__ = ' Base class for schema elements'
__eq__(value)

Return self==value.

__hash__ = None
__module__ = 'sas.system.config.schema_elements'
__weakref__

list of weak references to the object

_abc_impl = <_abc._abc_data object>
abstract coerce(value)

Force a variable to conform to the schema, if possible

validate(value: Any) bool

Return true if value is valid according to the schema

exception sas.system.config.schema_elements.SchemaError(message)

Bases: Exception

Raised when there are problems creating a schema

__doc__ = ' Raised when there are problems creating a schema'
__init__(message)
__module__ = 'sas.system.config.schema_elements'
__weakref__

list of weak references to the object

class sas.system.config.schema_elements.SchemaFloat

Bases: SchemaVariable

__abstractmethods__ = frozenset({})
__doc__ = None
__module__ = 'sas.system.config.schema_elements'
_abc_impl = <_abc._abc_data object>
coerce(value)

Force a variable to conform to the schema, if possible

schema_variable_type: str = 'float'
class sas.system.config.schema_elements.SchemaInt

Bases: SchemaVariable

__abstractmethods__ = frozenset({})
__doc__ = None
__module__ = 'sas.system.config.schema_elements'
_abc_impl = <_abc._abc_data object>
coerce(value)

Force a variable to conform to the schema, if possible

schema_variable_type: str = 'int'
class sas.system.config.schema_elements.SchemaList(child_type: SchemaElement)

Bases: SchemaElement

Schema Element representing a homogeneous list

__abstractmethods__ = frozenset({})
__doc__ = ' Schema Element representing a homogeneous list'
__eq__(other: SchemaElement)

Return self==value.

__hash__ = None
__init__(child_type: SchemaElement)
__module__ = 'sas.system.config.schema_elements'
__repr__()

Return repr(self).

_abc_impl = <_abc._abc_data object>
coerce(value)

Force a variable to conform to the schema, if possible

class sas.system.config.schema_elements.SchemaNonSpecified

Bases: SchemaElement

Representation of a list with elements of an unknown type - we use this when an empty list is in the config, or default is set to None

__abstractmethods__ = frozenset({})
__doc__ = ' Representation of a list with elements of an unknown type -\n    we use this when an empty list is in the config, or default is set to None'
__eq__(other: SchemaElement)

Return self==value.

__hash__ = None
__init__()
__module__ = 'sas.system.config.schema_elements'
__repr__()

Return repr(self).

_abc_impl = <_abc._abc_data object>
coerce(value)

Force a variable to conform to the schema, if possible

class sas.system.config.schema_elements.SchemaStr

Bases: SchemaVariable

__abstractmethods__ = frozenset({})
__doc__ = None
__module__ = 'sas.system.config.schema_elements'
_abc_impl = <_abc._abc_data object>
coerce(value)

Force a variable to conform to the schema, if possible

schema_variable_type: str = 'str'
class sas.system.config.schema_elements.SchemaVariable

Bases: SchemaElement

SchemaElement for values (i.e. float, int, bool, str)

__abstractmethods__ = frozenset({'coerce'})
__annotations__ = {'schema_variable_type': <class 'str'>}
__doc__ = ' SchemaElement for values (i.e. float, int, bool, str)'
__eq__(other: SchemaElement)

Return self==value.

__hash__ = None
__init__()
__module__ = 'sas.system.config.schema_elements'
__repr__()

Return repr(self).

_abc_impl = <_abc._abc_data object>
schema_variable_type: str = ''
sas.system.config.schema_elements.create_schema_element(name: str, value, recursion_depth: int = 10) SchemaElement

Get an appropriate schema element for a specified config datum

sas.system.config.schema_elements.pairwise_schema_union(a: SchemaElement | None, b: SchemaElement | None) SchemaElement | None

Pairwise union of Schema Elements

sas.system.config.schema_elements.schema_union(elements: List[SchemaElement])

Union of an arbitrary number of Schema Elements

Module contents