Skip to content

Commit acec360

Browse files
authored
Merge pull request #3166 from SasView/platfromdirs
Use platformdirs package for all user-related file paths
2 parents ad4d1e1 + 400ece0 commit acec360

File tree

16 files changed

+255
-96
lines changed

16 files changed

+255
-96
lines changed

build_tools/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ matplotlib
1616
numba
1717
numpy
1818
periodictable
19+
platformdirs
1920
pybind11
2021
pylint
2122
pyopengl

docs/sphinx-docs/build_sphinx.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
from distutils.util import get_platform
2020
from distutils.spawn import find_executable
2121

22-
from sas.system.user import get_user_dir
23-
2422
platform = '.%s-%s'%(get_platform(),sys.version[:3])
2523

2624
# sphinx paths

docs/sphinx-docs/source/user/working.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ Working with SasView
2525
Model Marketplace <marketplace>
2626

2727
Preferences <qtgui/MainWindow/preferences_help.rst>
28+
29+
User Files <qtgui/MainWindow/sasview_files>

installers/installer.iss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ end;
6161
6262
[Files]
6363
Source: "dist\sasview\sasview.exe"; DestDir: "{app}"; Flags: ignoreversion
64-
Source: "dist\sasview\plugin_models\*"; DestDir: "{%USERPROFILE}\.sasview\plugin_models"
64+
Source: "dist\sasview\plugin_models\*"; DestDir: "{%USERPROFILE}\AppData\Local\sasview\SasView\plugin_models"
6565
Source: "dist\sasview\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
6666

6767
[InstallDelete]

installers/sasview.spec

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ datas = [
1919
('../src/sas/qtgui/Utilities/WhatsNew/messages', 'sas/qtgui/Utilities/WhatsNew/messages'),
2020
('../src/sas/qtgui/Utilities/WhatsNew/css/style.css', 'sas/qtgui/Utilities/WhatsNew/css'),
2121
('../src/sas/qtgui/Utilities/About/images', 'sas/qtgui/Utilities/About/images'),
22-
('../src/sas/system/log.ini', 'sas/system/'),
2322
('../../sasmodels/sasmodels','sasmodels'),
2423
('../docs/sphinx-docs/build','doc/build'),
2524
('../docs/sphinx-docs/source-temp','doc/source')

src/sas/cli.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ def parse_cli(argv):
104104
return opts
105105

106106
def main(logging="production"):
107+
# Copy files before loading config
108+
from sas.system.user import copy_old_files_to_new_location
109+
copy_old_files_to_new_location()
110+
107111
from sas.system import log
108112
from sas.system import lib
109113
from sas.system import console
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
.. sasview_files.rst
2+
3+
.. Initial Draft: J Krzywon, Apr 2025
4+
.. Last Updated: J Krzywon, Apr. 9, 2025
5+
6+
.. _UserFiles:
7+
8+
SasView User Files
9+
==================
10+
11+
During the installation process and while running the app, SasView creates a number of files that are stored outside
12+
the install location on the end users computer. These files include configurations, plugin models, compiled plugins,
13+
documentation, and example files. Please refer to the specific section to find where each type of file is stored.
14+
15+
File paths that start with the '~' character refer to the user directory, which is typically `C:\\\\Users\\<username>` in
16+
Windows, `/Users/<username>` in MacOS, and `/home/<username>` in most Linux distributions.
17+
18+
This document is specific to v6.1.0 and beyond. Previous versions of SasView stored user files in the
19+
`~/.sasview` directory.
20+
21+
**For users who wish to use versions both newer and older than v6.1, do not delete the contents
22+
of the `~/.sasview` directory.**
23+
24+
25+
:ref:`Config_Files`, :ref:`Log_Files`, :ref:`Plugin_Files`, :ref:`Compiled_Files`, :ref:`Documentation`
26+
27+
.. , :ref:`Example_Data`
28+
29+
.. _Config_Files:
30+
31+
Configuration Files
32+
--------------------
33+
SasView stores two types of config file on the user disk, a general configuration file for each major version of sasview,
34+
and a categories file that allows the end user to change how their models are organized in the fitting perspective. The
35+
general configuration file, config-<v>.json is a json file that stores a mapping of config variables to their updated values.
36+
This file is only generated and stored locally if something in the :ref:`Preferences` panel has been modified. The categories
37+
file, categories.json, stores the user-preferred model organization, allowing a user to add model categories, move models
38+
into new categories, and not show certain models. More information on this feature is available in :ref:`Category_Manager`.
39+
40+
OS-specific file locations for configuration files:
41+
- Windows: ~/AppData/Local/sasview/SasView/
42+
- MacOS: ~/Library/Application Support/SasView/
43+
- Linux: ~/.config/SasView/
44+
45+
.. _Log_Files:
46+
47+
Log Files
48+
---------
49+
SasView creates a time-stamped log file that includes all messages entered into the log explorer, as well as a number of
50+
other messages related to application startup and shutdown.
51+
52+
OS-specific file locations for log files:
53+
- Windows: ~/AppData/Local/sasview/SasView/Logs/
54+
- MacOS: ~/Library/Logs/SasView/
55+
- Linux: ~/.local/state/SasView/
56+
57+
.. _Plugin_Files:
58+
59+
Plugin Files
60+
------------
61+
Plugin models, whether they be written by the user (:ref:`Writing_a_Plugin`), downloaded from the
62+
`model marketplace <https://marketplace.sasview.org/>`_, or sent from another SasView user, should be stored in a
63+
specific location to allow SasView to find them.
64+
65+
OS-specific file locations for plugin files:
66+
- Windows: ~/AppData/Local/sasview/SasView/plugin_models/
67+
- MacOS: ~/Library/Application Support/SasView/plugin_models/
68+
- Linux: ~/.local/share/SasView/plugin_models
69+
70+
.. _Compiled_Files:
71+
72+
Complied Model Files
73+
------------
74+
The fitting module, when a model is selected, compiles each model in an on-demand process. The compiled file will include
75+
all form factors and structure factors used to create the model, and will be compiled based on the GPU optimization
76+
currently in use. These models are stored in a parallel directory to the plugin models, but end users do not need to add
77+
any files to this directory.
78+
79+
OS-specific file locations for compiled model files:
80+
- Windows: ~/AppData/Local/sasview/SasView/compiled_models/
81+
- MacOS: ~/Library/Application Support/SasView/compiled_models/
82+
- Linux: ~/.local/share/SasView/compiled_models
83+
84+
.. _Documentation:
85+
86+
Documentation
87+
-------------
88+
SasView moves its documentation into a user location when launching the app, if the documentation does not already exist.
89+
Each version of SasView has its own documentation that may be different between versions, so a separate directory is used
90+
for each version of the doc files.
91+
92+
OS-specific file locations for documentation:
93+
- Windows: ~/AppData/Local/sasview/SasView/<sasview.version>/doc/
94+
- MacOS: ~/Library/Application Support/SasView/<sasview.version>/doc/
95+
- Linux: ~/.local/share/SasView/<sasview.version>/doc/
96+
97+
..
98+
.. _Example_Data:
99+
100+
.. Example Data
101+
.. ------------
102+
.. SasView supplies a number of example data files that may be used to orient yourself with the application. More information
103+
.. on the included files is available at :ref:`example_data_help`. These files are moved to the user directory on install.
104+
105+
.. OS-specific file locations for example data:
106+
.. - Windows: ~/AppData/Local/sasview/SasView/example_data/
107+
.. - MacOS: ~/Library/Application Support/SasView/example_data/
108+
.. - Linux: <TODO>

src/sas/qtgui/Utilities/CategoryInstaller.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import logging
1515
from collections import defaultdict, OrderedDict
1616

17+
from sas.system.user import get_config_dir
18+
1719
USER_FILE = 'categories.json'
1820

1921
class CategoryInstaller:
@@ -31,7 +33,7 @@ def _get_home_dir():
3133
"""
3234
returns the users sasview config dir
3335
"""
34-
return os.path.join(os.path.expanduser("~"), ".sasview")
36+
return get_config_dir()
3537

3638
@staticmethod
3739
def _regenerate_model_dict(master_category_dict):

src/sas/sascalc/doc_regen/makedocumentation.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,16 @@
1212
from typing import Union
1313

1414
from sas.sascalc.fit import models
15-
from sas.system.version import __version__
16-
from sas.system.user import get_user_dir
15+
from sas.system.user import get_app_dir_versioned
1716

1817
from sasmodels.core import list_models
1918

2019
PATH_LIKE = Union[Path, str, os.PathLike]
2120

2221
# Path constants related to the directories and files used in documentation regeneration processes
23-
USER_DIRECTORY = Path(get_user_dir())
24-
USER_DOC_BASE = USER_DIRECTORY / "doc"
25-
USER_DOC_SRC = USER_DOC_BASE / str(__version__)
22+
APP_DIRECTORY = Path(get_app_dir_versioned())
23+
USER_DOC_BASE = APP_DIRECTORY / "doc"
24+
USER_DOC_SRC = USER_DOC_BASE
2625
USER_DOC_LOG = USER_DOC_SRC / 'log'
2726
DOC_LOG = USER_DOC_LOG / 'output.log'
2827
MAIN_DOC_SRC = USER_DOC_SRC / "source-temp"

src/sas/sascalc/doc_regen/regenmodel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ def main():
413413
help="model files ")
414414
args = parser.parse_args()
415415

416-
TARGET_DIR = os.path.expanduser(args.rst)
416+
TARGET_DIR = Path(TARGET_DIR / args.rst) if args.rst else TARGET_DIR
417417
if not os.path.exists(TARGET_DIR) and not args.sphinx:
418418
print("build directory %r does not exist"%TARGET_DIR)
419419
sys.exit(1)

src/sas/sascalc/fit/models.py

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
"""
22
Utilities to manage models
33
"""
4-
from __future__ import print_function
5-
64
import os
75
import sys
86
import time
@@ -15,7 +13,7 @@
1513

1614
from sasmodels.sasview_model import load_custom_model, load_standard_models
1715

18-
from sas.system.user import get_user_dir
16+
from sas.system.user import get_plugin_dir
1917

2018
# Explicitly import from the pluginmodel module so that py2exe
2119
# places it in the distribution. The Model1DPlugin class is used
@@ -26,7 +24,7 @@
2624

2725

2826
PLUGIN_DIR = 'plugin_models'
29-
PLUGIN_LOG = os.path.join(get_user_dir(), PLUGIN_DIR, "plugins.log")
27+
PLUGIN_LOG = os.path.join(get_plugin_dir(), "plugins.log")
3028
PLUGIN_NAME_BASE = '[plug-in] '
3129

3230

@@ -82,29 +80,14 @@ def _check_plugin(model, name):
8280
return model
8381

8482

85-
def find_plugins_dir():
86-
"""
87-
Find path of the plugins directory.
88-
The plugin directory is located in the user's home directory.
83+
def find_plugins_dir() -> str:
84+
"""A helper function that returns a string representation of the plugins directory as defined by sas.system.user.
8985
"""
90-
path = os.path.join(os.path.expanduser("~"), '.sasview', PLUGIN_DIR)
91-
92-
# TODO: trigger initialization of plugins dir from installer or startup
93-
# If the plugin directory doesn't exist, create it
94-
if not os.path.isdir(path):
95-
os.makedirs(path)
96-
# TODO: should we be checking for new default models every time?
97-
# TODO: restore support for default plugins
98-
#initialize_plugins_dir(path)
99-
return path
86+
return str(get_plugin_dir())
10087

10188

10289
def initialize_plugins_dir(path):
10390
# TODO: There are no default plugins
104-
# TODO: Default plugins directory is in sasgui, but models.py is in sascalc
105-
# TODO: Move default plugins beside sample data files
106-
# TODO: Should not look for defaults above the root of the sasview install
107-
10891
# Walk up the tree looking for default plugin_models directory
10992
base = os.path.abspath(os.path.dirname(__file__))
11093
for _ in range(12):

src/sas/system/config/config_meta.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def defaults(self):
5858
def config_filename(self, create_if_nonexistent=False):
5959
"""Filename for saving config items"""
6060
version_parts = sas.system.version.__version__.split(".")
61-
user_dir = user.get_user_dir(create_if_nonexistent)
61+
user_dir = user.get_config_dir(create_if_nonexistent)
6262
return os.path.join(user_dir, f"config-{version_parts[0]}.json")
6363

6464
def finalise(self):

src/sas/system/lib.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# TODO: Add api to control sasmodels rather than using environment variables
99
def setup_sasmodels():
1010
"""Initialize sasmodels settings from the sasview configuration."""
11-
from .user import get_user_dir
11+
from .user import get_app_dir
1212

1313
# Don't need to set SAS_MODELPATH for gui because sascalc.fit uses the
1414
# full paths to models, but when using the sasview package as a python
@@ -21,7 +21,7 @@ def setup_sasmodels():
2121
# Both scripts and gui need to know the stored DLL path.
2222
if "SAS_DLL_PATH" not in os.environ:
2323
os.environ["SAS_DLL_PATH"] = os.path.join(
24-
get_user_dir(), "compiled_models")
24+
get_app_dir(), "compiled_models")
2525

2626
# Set OpenCL config from environment variable if it is set otherwise
2727
# use the value from the sas config file.

src/sas/system/log.ini

Lines changed: 0 additions & 45 deletions
This file was deleted.

src/sas/system/log.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import sys
77
import os.path
88

9-
import importlib.resources
9+
from sas.system.user import get_log_dir
1010

1111
'''
1212
Module that manages the global logging
@@ -42,12 +42,16 @@ def setup_logging(level=logging.INFO):
4242

4343
# Apply the logging config after setting the defaults
4444
try:
45-
fd = importlib.resources.open_text('sas.system', 'log.ini')
46-
logging.config.fileConfig(fd)
47-
except FileNotFoundError:
48-
print(f"ERROR: Log config '{fd.name}' not found...", file=sys.stderr)
49-
50-
#print_config()
45+
filename = os.path.join(get_log_dir(), 'sasview.log')
46+
fh = logging.FileHandler(filename=filename, mode="a")
47+
except IOError:
48+
print(f"ERROR: Log config '{filename}' not found...", file=sys.stderr)
49+
fh = None
50+
ch = logging.StreamHandler()
51+
handlers = [fh, ch] if fh else [ch]
52+
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(name)s (%(filename)s:%(lineno)s) :: %(message)s',
53+
handlers=handlers)
54+
# print_config()
5155

5256
def production():
5357
setup_logging('INFO')

0 commit comments

Comments
 (0)