Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
F
fslpy
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Deploy
Releases
Container Registry
Model registry
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Michiel Cottaar
fslpy
Commits
a6015d8c
Commit
a6015d8c
authored
7 years ago
by
Paul McCarthy
Browse files
Options
Downloads
Patches
Plain Diff
settings module rewritten so it no longer uses wx.
parent
719595f7
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
fsl/utils/settings.py
+280
-75
280 additions, 75 deletions
fsl/utils/settings.py
with
280 additions
and
75 deletions
fsl/utils/settings.py
+
280
−
75
View file @
a6015d8c
...
...
@@ -4,119 +4,324 @@
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""
This module provides a simple API to :func:`read`, :func:`write`,
:func:`delete`, and :func:`clear` persistent application settings.
.. note:: Currently the configuration management API provided by :mod:`wx`
(http://docs.wxwidgets.org/trunk/overview_config.html) is used for
storing application settings. This means that it is not possible
to persist settings from a non-GUI application.
But that
'
s the whole point of this module, to abstract away the
underlying persistence method. In the future I will replace
``wx.Config`` with something that does not rely upon the presence
of ``wx``.
"""
This module provides functions for storing and retrieving persistent
configuration settings and data files.
The :func:`initialise` function must be called to initialise the module. Then,
the following functions can be called at the module-level:
.. autosummary::
:nosignatures:
Settings.read
Settings.write
Settings.delete
Settings.readFile
Settings.writeFile
Settings.deleteFile
Settings.clear
These functions will have no effect before :func:`initialise` is called.
Two types of configuration data are available:
- Key-value pairs - access these via the ``read``, ``write`` and ``delete``
functions. These are stored in a single file, via ``pickle``. Anything
that can be pickled can be stored.
- Separate files, either text or binary. Access these via the ``readFile``,
``writeFile`, and ``deleteFile` functions.
Both of the above data types will be stored in a configuration directory.
The location of this directory differs from platform to platform, but is
likely to be either `~/.fslpy/` or `~/.config/fslpy/`.
"""
import
logging
from
__future__
import
absolute_import
from
.platform
import
platform
as
fslplatform
import
os
import
os.path
as
op
import
sys
import
atexit
import
shutil
import
pickle
import
logging
import
tempfile
import
platform
log
=
logging
.
getLogger
(
__name__
)
_CONFIG_ID
=
'
uk.ac.ox.fmrib.fslpy
'
"""
The configuration identifier passed to ``wx.Config``. This identifier
should be the same as the identifier given to the OSX application bundle
(see https://git.fmrib.ox.ac.uk/paulmc/fslpy_build).
_CONFIG_ID
=
'
fslpy
'
"""
The default configuration identifier, used as the directory name for
storing configuration files.
"""
def
strToBool
(
s
):
"""
Currently the ``settings`` module is not type aware, so boolean
values are saved as strings ``
'
True
'
`` or ``
'
False
'
``. This makes
conversion back to boolean a bit annoying, as ``
'
False
'
`` evaluates
to ``True``.
def
initialise
(
*
args
,
**
kwargs
):
"""
Initialise the ``settings`` module. This function creates a
:class:`Settings` instance, and enables the module-level
functions. All settings are passed through to :meth:`Settings.__init__`.
"""
mod
=
sys
.
modules
[
__name__
]
settings
=
Settings
(
*
args
,
**
kwargs
)
mod
.
settings
=
settings
mod
.
read
=
settings
.
read
mod
.
write
=
settings
.
write
mod
.
delete
=
settings
.
delete
mod
.
readFile
=
settings
.
readFile
mod
.
writeFile
=
settings
.
writeFile
mod
.
deleteFile
=
settings
.
deleteFile
mod
.
clear
=
settings
.
clear
# These are all overwritten by
# the initialise function.
def
read
(
*
args
,
**
kwargs
):
pass
def
write
(
*
args
,
**
kwargs
):
pass
def
delete
(
*
args
,
**
kwargs
):
pass
def
readFile
(
*
args
,
**
kwargs
):
pass
def
writeFile
(
*
args
,
**
kwargs
):
pass
def
deleteFile
(
*
args
,
**
kwargs
):
pass
def
clear
(
*
args
,
**
kwarg
):
pass
class
Settings
(
object
):
"""
The ``Settings`` class contains all of the logic provided by the
``settings`` module. It is not meant to be instantiated directly
(although you may do so if you wish).
.. autosummary::
:nosignatures:
read
write
delete
readFile
writeFile
deleteFile
clear
"""
This function may be used for a more sensible `str` -> `bool`
conversion
.. note:: In the future, the ``settings`` module will hopefully be
type-aware, so use of this function will no longer be necessary.
"""
s
=
str
(
s
).
lower
()
if
s
==
'
true
'
:
return
True
elif
s
==
'
false
'
:
return
False
else
:
return
bool
(
s
)
def
__init__
(
self
,
cfgid
=
_CONFIG_ID
,
cfgdir
=
None
,
writeOnExit
=
True
):
"""
Create a ``Settings`` instance.
:arg cfgid: Configuration ID, used as the name of the
configuration directory.
def
read
(
name
,
default
=
None
):
"""
Reads a setting with the given ``name``, return ``default`` if
there is no setting called ``name``.
"""
:arg cfgdir: Store configuration settings in this directory,
instead of the default.
if
not
fslplatform
.
haveGui
:
return
default
:arg writeOnExit: If ``True`` (the default), an ``atexit`` function
is registered, which calls :meth:`writeConfigFile`.
"""
import
wx
if
cfgdir
is
None
:
cfgdir
=
self
.
__getConfigDir
(
cfgid
)
config
=
wx
.
Config
(
_CONFIG_ID
)
value
=
config
.
Read
(
name
)
self
.
__configID
=
cfgid
self
.
__configDir
=
cfgdir
self
.
__config
=
self
.
__readConfigFile
(
)
log
.
debug
(
'
Read {}/{}: {}
'
.
format
(
_CONFIG_ID
,
name
,
'
(no value)
'
if
value
==
''
else
value
))
if
writeOnExit
:
atexit
.
register
(
self
.
writeConfigFile
)
if
value
==
''
:
return
default
else
:
return
value
@property
def
configID
(
self
):
"""
Returns the configuration identifier.
"""
return
self
.
__configID
def
write
(
name
,
value
):
"""
Writes a setting with the given ``name`` and ``value``.
"""
if
not
fslplatform
.
haveGui
:
return
@property
def
configDir
(
self
):
"""
Returns the location of the configuration directory.
"""
return
self
.
__configDir
import
wx
value
=
str
(
value
)
config
=
wx
.
Config
(
_CONFIG_ID
)
def
read
(
self
,
name
,
default
=
None
):
"""
Reads a setting with the given ``name``, return ``default`` if
there is no setting called ``name``.
"""
log
.
debug
(
'
Writing {}/{}: {}
'
.
format
(
_CONFIG_ID
,
name
,
value
))
log
.
debug
(
'
Reading {}/{}
'
.
format
(
self
.
__configID
,
name
))
return
self
.
__config
.
get
(
name
,
default
)
config
.
Write
(
name
,
value
)
def
write
(
self
,
name
,
value
):
"""
Writes the given ``value`` to the given file ``path``.
"""
def
delete
(
name
):
"""
Delete the setting with the given ``name``.
"""
log
.
debug
(
'
Writing {}/{}: {}
'
.
format
(
self
.
__configID
,
name
,
value
))
self
.
__config
[
name
]
=
value
if
not
fslplatform
.
haveGui
:
return
import
wx
def
delete
(
self
,
name
):
"""
Delete the setting with the given ``name``.
"""
config
=
wx
.
Config
(
_CONFIG_ID
)
log
.
debug
(
'
Deleting {}/{}
'
.
format
(
self
.
__configID
,
name
))
self
.
__config
.
pop
(
name
,
None
)
log
.
debug
(
'
Deleting {}/{}
'
.
format
(
_CONFIG_ID
,
name
))
config
.
DeleteEntry
(
name
)
def
readFile
(
self
,
path
,
mode
=
'
t
'
):
"""
Reads and returns the contents of the given file ``path``.
Returns ``None`` if the path does not exist.
"""
mode
=
'
r
'
+
mode
path
=
self
.
__fixPath
(
path
)
path
=
op
.
join
(
self
.
__configDir
,
path
)
if
op
.
exists
(
path
):
with
open
(
path
,
mode
)
as
f
:
return
f
.
read
()
else
:
return
None
def
writeFile
(
self
,
path
,
value
,
mode
=
'
t
'
):
"""
Writes the given ``value`` to the given file ``path``.
"""
mode
=
'
w
'
+
mode
path
=
self
.
__fixPath
(
path
)
path
=
op
.
join
(
self
.
__configDir
,
path
)
pathdir
=
op
.
dirname
(
path
)
if
not
op
.
exists
(
pathdir
):
os
.
makedirs
(
pathdir
)
with
open
(
path
,
mode
)
as
f
:
f
.
write
(
value
)
def
deleteFile
(
self
,
path
):
"""
Deletes the given file ``path``.
"""
path
=
self
.
__fixPath
(
path
)
path
=
op
.
join
(
self
.
__configDir
,
path
)
if
op
.
exists
(
path
):
os
.
remove
(
path
)
def
clear
(
self
):
"""
Delete all configuration settings and files.
"""
log
.
debug
(
'
Clearing all settings in {}
'
.
format
(
self
.
__configID
))
self
.
__config
=
{}
for
path
in
os
.
listdir
(
self
.
__configDir
):
path
=
op
.
join
(
self
.
__configDir
,
path
)
if
op
.
isdir
(
path
):
shutil
.
rmtree
(
path
)
else
:
os
.
remove
(
path
)
def
__fixPath
(
self
,
path
):
"""
Ensures that the given path (passed into :meth:`readFile`,
:meth:`writeFile`, or :meth:`deleteFile`) is cross-platform
compatible.
"""
return
op
.
join
(
*
path
.
split
(
'
/
'
))
def
__getConfigDir
(
self
,
cid
):
"""
Returns a directory in which configuration files can be stored.
.. note:: If, for whatever reason, a configuration directory could not
be located or created, a temporary directory will be used.
This means that all settings read during this session will
be lost on exit.
"""
cfgdir
=
None
homedir
=
op
.
expanduser
(
'
~
'
)
# On linux, if $XDG_CONFIG_HOME is set, use $XDG_CONFIG_HOME/fslpy/
# Otherwise, use $HOME/.config/fslpy/
#
# https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
if
platform
.
system
().
lower
().
startswith
(
'
linux
'
):
basedir
=
os
.
environ
.
get
(
'
XDG_CONFIG_HOME
'
)
if
basedir
is
None
:
basedir
=
op
.
join
(
homedir
,
'
.config
'
)
cfgdir
=
op
.
join
(
basedir
,
cid
)
# On all other platforms, use $HOME/.fslpy/
else
:
cfgdir
=
op
.
join
(
homedir
,
'
.{}
'
.
format
(
cid
))
# Try and create the config directory
# tree if it does not exist
if
not
op
.
exists
(
cfgdir
):
try
:
os
.
makedirs
(
cfgdir
)
except
:
log
.
warning
(
'
Unable to create {} configuration
'
'
directory: {}
'
.
format
(
cid
,
cfgdir
),
exc_info
=
True
)
cfgdir
=
None
# If dir creation failed, use a temporary
# directory, and delete it on exit
if
cfgdir
is
None
:
cfgdir
=
tempfile
.
mkdtemp
()
atexit
.
register
(
shutil
.
rmtree
,
cfgdir
,
ignore_errors
=
True
)
log
.
debug
(
'
{} configuration directory: {}
'
.
format
(
cid
,
cfgdir
))
return
cfgdir
def
clear
():
"""
Delete all settings.
"""
if
not
fslplatform
.
haveGui
:
return
import
wx
def
__readConfigFile
(
self
):
"""
Called by :meth:`__init__`. Reads any settings that were stored
in a file, and returns them in a dictionary.
"""
config
=
wx
.
Config
(
_CONFIG_ID
)
config
File
=
op
.
join
(
self
.
__configDir
,
'
config.pkl
'
)
log
.
debug
(
'
Clearing all settings in {}
'
.
format
(
_CONFIG_ID
))
log
.
debug
(
'
Reading {} configuration from: {}
'
.
format
(
self
.
__configID
,
configFile
))
try
:
with
open
(
configFile
,
'
rb
'
)
as
f
:
return
pickle
.
load
(
f
)
except
:
log
.
warning
(
'
Unable to load stored {} configuration file
'
'
{}
'
.
format
(
self
.
__configID
,
configFile
),
exc_info
=
True
)
return
{}
config
.
DeleteAll
()
def
writeConfigFile
(
self
):
"""
Writes all settings to a file.
"""
config
=
self
.
__config
configFile
=
op
.
join
(
self
.
__configDir
,
'
config.pkl
'
)
log
.
debug
(
'
Writing {} configuration to: {}
'
.
format
(
self
.
__configID
,
configFile
))
try
:
with
open
(
configFile
,
'
wb
'
)
as
f
:
pickle
.
dump
(
config
,
f
)
except
:
log
.
warning
(
'
Unable to save {} configuration file
'
'
{}
'
.
format
(
self
.
__configID
,
configFile
),
exc_info
=
True
)
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment