diff --git a/MDANSE/Src/MDANSE/Chemistry/ChemicalSystem.py b/MDANSE/Src/MDANSE/Chemistry/ChemicalSystem.py
index d411393640..9b8348f913 100644
--- a/MDANSE/Src/MDANSE/Chemistry/ChemicalSystem.py
+++ b/MDANSE/Src/MDANSE/Chemistry/ChemicalSystem.py
@@ -36,7 +36,7 @@ def __init__(self, name: str = "", trajectory=None):
:type name: str
"""
- self._name = name
+ self.name = str(name)
self._database = ATOMS_DATABASE
if trajectory is not None:
self._database = trajectory
@@ -55,7 +55,7 @@ def __init__(self, name: str = "", trajectory=None):
self._unique_elements = set()
def __str__(self):
- return f"ChemicalSystem {self._name} consisting of {len(self._atom_types)} atoms in {len(self._clusters)} molecules"
+ return f"ChemicalSystem {self.name} consisting of {len(self._atom_types)} atoms in {len(self._clusters)} molecules"
def initialise_atoms(self, element_list: List[str], name_list: List[str] = None):
self._atom_indices = [
@@ -191,7 +191,7 @@ def copy(self) -> "ChemicalSystem":
:return: Copy of the ChemicalSystem instance
:rtype: MDANSE.Chemistry.ChemicalSystem.ChemicalSystem
"""
- cs = ChemicalSystem(self._name)
+ cs = ChemicalSystem(self.name)
for attribute_name, attribute_value in self.__dict__.items():
if attribute_name in ["rdkit_mol", "_configuration"]:
@@ -248,7 +248,7 @@ def serialize(self, h5_file: h5py.File) -> None:
string_dt = h5py.special_dtype(vlen=str)
grp = h5_file.create_group("/composition")
- grp.attrs["name"] = self._name
+ grp.attrs["name"] = self.name
try:
grp.create_dataset("atom_types", data=self._atom_types, dtype=string_dt)
@@ -292,7 +292,7 @@ def load(self, trajectory: str):
self.rdkit_mol = Chem.RWMol()
grp = source["/composition"]
- self._name = grp.attrs["name"]
+ self.name = grp.attrs["name"]
atom_types = [binary.decode("utf-8") for binary in grp["atom_types"][:]]
atom_names = None
@@ -326,7 +326,7 @@ def legacy_load(self, trajectory: str):
self.rdkit_mol = Chem.RWMol()
grp = source["/chemical_system"]
- self._name = grp.attrs["name"]
+ self.name = grp.attrs["name"]
atoms = grp["atoms"]
element_list = [line[0].decode("utf-8").strip("'") for line in atoms]
self.initialise_atoms(element_list)
diff --git a/MDANSE/Src/MDANSE/Chemistry/Databases.py b/MDANSE/Src/MDANSE/Chemistry/Databases.py
index df6f51204c..e7b52792d4 100644
--- a/MDANSE/Src/MDANSE/Chemistry/Databases.py
+++ b/MDANSE/Src/MDANSE/Chemistry/Databases.py
@@ -15,8 +15,8 @@
#
import copy
-import os
from typing import Union, ItemsView, Dict, Any
+from pathlib import Path
import json
@@ -30,8 +30,8 @@ class _Database(metaclass=Singleton):
Base class for all the databases.
"""
- _DEFAULT_DATABASE: str
- _USER_DATABASE: str
+ _DEFAULT_DATABASE: Path
+ _USER_DATABASE: Path
def __init__(self):
"""
@@ -65,7 +65,11 @@ def __iter__(self):
for v in self._data.values():
yield copy.deepcopy(v)
- def _load(self, user_database: str = None, default_database: str = None) -> None:
+ def _load(
+ self,
+ user_database: Union[Path, str, None] = None,
+ default_database: Union[Path, str, None] = None,
+ ) -> None:
"""
Load the database. This method should never be called elsewhere than __init__ or unit testing.
@@ -77,10 +81,14 @@ def _load(self, user_database: str = None, default_database: str = None) -> None
"""
if user_database is None:
user_database = self._USER_DATABASE
+ else:
+ user_database = Path(user_database)
if default_database is None:
default_database = self._DEFAULT_DATABASE
+ else:
+ default_database = Path(default_database)
- if os.path.exists(user_database):
+ if user_database.exists():
database_path = user_database
else:
database_path = default_database
@@ -164,10 +172,10 @@ class AtomsDatabase(_Database):
>>> atoms = ATOMS_DATABASE.atoms()
"""
- _DEFAULT_DATABASE = os.path.join(os.path.dirname(__file__), "atoms.json")
+ _DEFAULT_DATABASE = Path(__file__).parent / "atoms.json"
# The user path
- _USER_DATABASE = os.path.join(PLATFORM.application_directory(), "atoms.json")
+ _USER_DATABASE = PLATFORM.application_directory() / "atoms.json"
# The python types supported by the database
_TYPES = {"str": str, "int": int, "float": float, "list": list}
diff --git a/MDANSE/Src/MDANSE/Core/Platform.py b/MDANSE/Src/MDANSE/Core/Platform.py
index 9a0fb9d92e..96077c3fd3 100644
--- a/MDANSE/Src/MDANSE/Core/Platform.py
+++ b/MDANSE/Src/MDANSE/Core/Platform.py
@@ -20,13 +20,16 @@
import getpass
import inspect
import os
+import platform
import re
import subprocess
-import tempfile
-
+from pathlib import Path
+from typing import Optional, Union
from MDANSE.Core.Error import Error
+PathLike = Union[Path, str]
+
class PlatformError(Error):
"""
@@ -58,28 +61,28 @@ def __new__(cls, *args, **kwargs):
return cls.__instance
@abc.abstractmethod
- def application_directory(self):
+ def application_directory(self) -> Path:
"""
Returns the path for MDANSE application directory.
The directory data used by MDANSE for storing preferences, databses, jobs temporary files ...
:return: the path for MDANSE application directory.
- :rtype: str
+ :rtype: Path
"""
pass
- def doc_path(self):
+ def doc_path(self) -> Path:
"""
Returns the path for MDANSE documentation root directory.
:return: the path for MDANSE documentation root directory
- :rtype: str
+ :rtype: Path
"""
- return os.path.join(self.package_directory(), "Doc")
+ return self.package_directory() / "Doc"
- def jobs_launch_delay(self):
+ def jobs_launch_delay(self) -> float:
"""
Returns the delay (in seconds) for a job to launch.
This is used to determine the delay before updating the GUI and suppressing a job status file
@@ -89,27 +92,27 @@ def jobs_launch_delay(self):
"""
return 2.0
- def api_path(self):
+ def api_path(self) -> Path:
"""
Returns the path for MDANSE HTML API.
:return: the path for MDANSE HTML documentation
- :rtype: str
+ :rtype: Path
"""
- return os.path.join(self.package_directory(), "Doc", "api", "html")
+ return self.package_directory() / "Doc" / "api" / "html"
- def help_path(self):
+ def help_path(self) -> Path:
"""
Returns the path for MDANSE HTML help.
:return: the path for MDANSE HTML documentation
- :rtype: str
+ :rtype: Path
"""
- return os.path.join(self.package_directory(), "Doc", "help", "html")
+ return self.package_directory() / "Doc" / "help" / "html"
- def full_dotted_module(self, obj):
+ def full_dotted_module(self, obj) -> Optional[str]:
"""
Returns the fully dotted name of a module given the module object itself or a class stored in this module.
@@ -121,25 +124,22 @@ def full_dotted_module(self, obj):
"""
if inspect.ismodule(obj):
- path = obj.__file__
+ path = Path(obj.__file__)
elif inspect.isclass(obj):
- path = inspect.getmodule(obj).__file__
+ path = Path(inspect.getmodule(obj).__file__)
else:
raise PlatformError("Invalid query object type.")
- basepath = os.path.join(os.path.dirname(self.package_directory()), "")
+ basepath = self.package_directory().parent
- s = path.split(basepath)
- if len(s) != 2:
+ try:
+ relativePath = path.relative_to(basepath)
+ except ValueError:
return None
- _, relativePath = path.split(basepath)
+ return ".".join(relativePath.with_suffix("").parts)
- relativePath = os.path.splitext(relativePath)[0]
-
- return ".".join(relativePath.split(os.path.sep))
-
- def change_directory(self, directory):
+ def change_directory(self, directory: PathLike) -> None:
"""
Change the current directory to a new directory.
@@ -150,7 +150,7 @@ def change_directory(self, directory):
os.chdir(directory)
@classmethod
- def is_file_writable(cls, filepath: str) -> bool:
+ def is_file_writable(cls, filepath: PathLike) -> bool:
"""Check if the directories can be created and a file can be
written into it.
@@ -164,39 +164,15 @@ def is_file_writable(cls, filepath: str) -> bool:
bool
True if a file can be written.
"""
- dirname = cls.get_path(os.path.dirname(filepath))
+ filepath = cls.get_path(filepath)
- def recursive_check(head_0: str) -> bool:
- """Builds the directories up and tests if the file can be
- written and then removes everything so that no changes are
- made to the filesystem.
- """
- if os.path.exists(dirname):
- try:
- open(filepath, "w").close()
- os.remove(filepath)
- except OSError:
- return False
- return True
-
- head, tail = os.path.split(head_0)
- if os.path.exists(head):
- try:
- os.mkdir(head_0)
- except OSError:
- return False
- writable = recursive_check(dirname)
- os.rmdir(head_0)
- return writable
- else:
- return recursive_check(head)
-
- if os.path.isfile(filepath):
- return os.access(filepath, os.W_OK)
- else:
- return recursive_check(dirname)
+ for direc in filepath.parents:
+ if direc.exists():
+ return os.access(direc, os.W_OK)
- def create_directory(self, path):
+ return False
+
+ def create_directory(self, path: PathLike) -> None:
"""
Creates a directory.
@@ -206,32 +182,26 @@ def create_directory(self, path):
path = self.get_path(path)
- if os.path.exists(path):
- return
-
- # Try to make the directory.
try:
- os.makedirs(path)
-
+ path.mkdir(parents=True, exist_ok=True)
# An error occured.
- except OSError as e:
+ except OSError as err:
raise PlatformError(
- "The following exception was raised while trying to create a directory at "
- "{0}: /n {1}".format(str(path), e)
- )
+ f"Problem trying to create a directory at {path}"
+ ) from err
@classmethod
- def get_path(cls, path):
+ def get_path(cls, path: PathLike) -> Path:
"""
Return a normalized and absolute version of a given path
:param path: the path of the file to be normalized and made absolute
- :type path: str
+ :type path: Path
:return: the normalized and absolute version of the input path
- :rtype: str
+ :rtype: Path
"""
- return os.path.abspath(os.path.expanduser(path))
+ return Path(path).expanduser().absolute()
def database_default_path(self):
"""
@@ -241,7 +211,7 @@ def database_default_path(self):
:rtype: string
"""
- return os.path.join(self.package_directory(), "Data", "elements_database.csv")
+ return self.package_directory() / "Data" / "elements_database.csv"
def database_user_path(self):
"""
@@ -251,7 +221,7 @@ def database_user_path(self):
:rtype: string
"""
- return os.path.join(self.application_directory(), "elements_database.csv")
+ return self.application_directory() / "elements_database.csv"
@abc.abstractmethod
def get_processes_info(self):
@@ -291,7 +261,7 @@ def example_data_directory(self):
:rtype: str
"""
- return os.path.join(os.path.dirname(self.package_directory()), "Data")
+ return self.package_directory().parent / "Data"
def base_directory(self):
"""
@@ -301,59 +271,57 @@ def base_directory(self):
@rtype: str
"""
- return os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
+ return Path(__file__).parents[2]
- def package_directory(self):
+ def package_directory(self) -> Path:
"""
Returns the path for MDANSE package.
@return: the path for MDANSE package.
- @rtype: str
+ @rtype: Path
"""
- return os.path.dirname(os.path.dirname(__file__))
+ return Path(__file__).parent.parent
- def macros_directory(self):
+ def macros_directory(self) -> Path:
"""
Returns the path of the directory where the MDANSE macros will be searched.
:return: the path of the directory where the MDANSE macros will be searched.
- :rtype: str
+ :rtype: Path
"""
- macrosDir = os.path.join(self.application_directory(), "macros")
-
- return macrosDir
+ return self.application_directory() / "macros"
- def logfiles_directory(self):
+ def logfiles_directory(self) -> Path:
"""
Returns the path of the directory where the MDANSE job logfiles are stored.
:return: the path of the directory where the MDANSE job logfiles are stored.
- :rtype: str
+ :rtype: Path
"""
- path = os.path.join(self.application_directory(), "logfiles")
+ path = self.application_directory() / "logfiles"
self.create_directory(path)
return path
- def temporary_files_directory(self):
+ def temporary_files_directory(self) -> Path:
"""
Returns the path of the directory where the temporary MDANSE job status files are stored.
:return: the path of the directory where the temporary MDANSE job status files are stored
- :rtype: str
+ :rtype: Path
"""
- path = os.path.join(self.application_directory(), "temporary_files")
+ path = self.application_directory() / "temporary_files"
self.create_directory(path)
return path
- def username(self):
+ def username(self) -> str:
"""
Returns the name of the user that run MDANSE.
@@ -411,11 +379,10 @@ def application_directory(self):
:rtype: str
"""
- basedir = os.path.join(os.environ["HOME"], ".mdanse")
+ basedir = Path(os.environ["HOME"]) / ".mdanse"
# If the application directory does not exist, create it.
- if not os.path.exists(basedir):
- os.makedirs(basedir)
+ basedir.mkdir(exist_ok=True, parents=True)
return basedir
@@ -449,18 +416,15 @@ def get_processes_info(self):
"""
# Get all the active processes using the Unix ps command
- procs = subprocess.Popen(["ps", "-eo", "pid,etime"], stdout=subprocess.PIPE)
-
- # The output of the ps command is splitted according to line feeds.
- procs = procs.communicate()[0].decode("utf-8").split("\n")[1:]
-
- # The list of (pid,executable).
- procs = [p.split() for p in procs if p]
-
- # A mapping between the active processes pid and their corresponding exectuable.
- procs = dict(
- [(int(p[0].strip()), self.etime_to_ctime(p[1].strip())) for p in procs]
+ process = subprocess.run(
+ ["ps", "-eo", "pid,etime"],
+ capture_output=True,
+ check=True,
+ text=True,
+ shell=True,
)
+ procs = map(str.split, filter(None, process.stdout.splitlines()))
+ procs = {int(pid): self.etime_to_ctime(etime.strip()) for pid, etime in procs}
return procs
@@ -488,25 +452,24 @@ class PlatformWin(Platform):
name = "windows"
- def application_directory(self):
+ def application_directory(self) -> Path:
"""
Returns the path for MDANSE application directory.
The directory data used by MDANSE for storing preferences, databses, jobs temporary files ...
:return: the path for MDANSE application directory.
- :rtype: str
+ :rtype: Path
"""
- basedir = os.path.join(os.environ["APPDATA"], "mdanse")
+ basedir = Path(os.environ["APPDATA"]) / "mdanse"
# If the application directory does not exist, create it.
- if not os.path.exists(basedir):
- os.makedirs(basedir)
+ basedir.mkdir(parents=True, exist_ok=True)
return basedir
- def get_process_creation_time(self, process):
+ def get_process_creation_time(self, process) -> int:
"""
Return the creation time of a given process.
@@ -521,7 +484,7 @@ def get_process_creation_time(self, process):
exittime = ctypes.c_ulonglong()
kerneltime = ctypes.c_ulonglong()
usertime = ctypes.c_ulonglong()
- rc = ctypes.windll.kernel32.GetProcessTimes(
+ ctypes.windll.kernel32.GetProcessTimes(
process,
ctypes.byref(creationtime),
ctypes.byref(exittime),
@@ -534,7 +497,7 @@ def get_process_creation_time(self, process):
return creationtime.value
- def get_processes_info(self):
+ def get_processes_info(self) -> dict:
"""
Returns the current active processes.
@@ -563,7 +526,7 @@ def get_processes_info(self):
# Number of processes returned
nReturned = cbNeeded.value // ctypes.sizeof(ctypes.c_ulong())
- pidProcess = [i for i in aProcesses][:nReturned]
+ pidProcess = list(aProcesses)[:nReturned]
for pid in pidProcess:
# Get handle to the process based on PID
@@ -593,15 +556,15 @@ def get_processes_info(self):
return processes
- def home_directory(self):
+ def home_directory(self) -> Path:
"""
Returns the home directory of the user that runs MDANSE.
@return: the home directory
- @rtype: str
+ @rtype: Path
"""
- return os.environ["USERPROFILE"]
+ return Path(os.environ["USERPROFILE"])
def kill_process(self, pid):
"""
@@ -623,9 +586,8 @@ def kill_process(self, pid):
ctypes.windll.kernel32.CloseHandle(handle)
-import platform
-
system = platform.system()
+PLATFORM: Platform
# Instantiate the proper platform class depending on the OS on which MDANSE runs
if system == "Linux":
diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/AseInputFileConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/AseInputFileConfigurator.py
index ecefe18fee..d69e05bbfe 100644
--- a/MDANSE/Src/MDANSE/Framework/Configurators/AseInputFileConfigurator.py
+++ b/MDANSE/Src/MDANSE/Framework/Configurators/AseInputFileConfigurator.py
@@ -13,7 +13,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import os
from ase.io.formats import all_formats
@@ -63,7 +62,7 @@ def configure(self, values):
value = PLATFORM.get_path(value)
- if not os.path.exists(value):
+ if not value.exists():
LOG.error(f"FILE MISSING in {self._name}")
self.error_status = f"The file {value} does not exist"
return
diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/HDFTrajectoryConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/HDFTrajectoryConfigurator.py
index b58d8b1987..be357df2de 100644
--- a/MDANSE/Src/MDANSE/Framework/Configurators/HDFTrajectoryConfigurator.py
+++ b/MDANSE/Src/MDANSE/Framework/Configurators/HDFTrajectoryConfigurator.py
@@ -14,8 +14,6 @@
# along with this program. If not, see .
#
-import os
-
from MDANSE import PLATFORM
from MDANSE.Framework.Configurators.InputFileConfigurator import InputFileConfigurator
@@ -60,7 +58,7 @@ def configure(self, value):
self["filename"] = PLATFORM.get_path(inputTraj.filename)
- self["basename"] = os.path.basename(self["filename"])
+ self["basename"] = self["filename"].name
self["length"] = len(self["instance"])
diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/IConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/IConfigurator.py
index c77ea5c795..9372e7be0f 100644
--- a/MDANSE/Src/MDANSE/Framework/Configurators/IConfigurator.py
+++ b/MDANSE/Src/MDANSE/Framework/Configurators/IConfigurator.py
@@ -16,12 +16,22 @@
import abc
import json
+from pathlib import Path
from MDANSE.Core.Error import Error
from MDANSE.Core.SubclassFactory import SubclassFactory
+class CustomEncoder(json.JSONEncoder):
+ """Custom JSON encoder to encode paths as strings."""
+
+ def default(self, obj):
+ if isinstance(obj, Path):
+ return str(obj)
+ return super().default(obj)
+
+
class ConfiguratorError(Error):
"""
This class handles any exception related to Configurator-derived object
@@ -82,7 +92,7 @@ class IConfigurator(dict, metaclass=SubclassFactory):
_default = None
- _encoder = json.encoder.JSONEncoder()
+ _encoder = CustomEncoder()
_decoder = json.decoder.JSONDecoder()
_doc_ = "undocumented"
@@ -90,12 +100,12 @@ class IConfigurator(dict, metaclass=SubclassFactory):
def __init__(self, name, **kwargs):
"""
Initializes a configurator object.
-
+
:param name: the name of this configurator.
:type name: str
:param dependencies: the other configurators on which this configurator depends on to be configured. \
This has to be input as a dictionary that maps the name under which the dependency will be used within \
- the configurator implementation to the actual name of the configurator on which this configurator is depending on.
+ the configurator implementation to the actual name of the configurator on which this configurator is depending on.
:type dependencies: (str,str)-dict
:param default: the default value of this configurator.
:type default: any python object
diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/InputFileConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/InputFileConfigurator.py
index cd186bebac..eca5c5675b 100644
--- a/MDANSE/Src/MDANSE/Framework/Configurators/InputFileConfigurator.py
+++ b/MDANSE/Src/MDANSE/Framework/Configurators/InputFileConfigurator.py
@@ -14,8 +14,6 @@
# along with this program. If not, see .
#
-import os
-
from MDANSE import PLATFORM
from MDANSE.Framework.Configurators.IConfigurator import IConfigurator
@@ -54,7 +52,7 @@ def configure(self, value):
value = PLATFORM.get_path(value)
- if not os.path.exists(value):
+ if not value.exists():
self.error_status = f"The file {value} does not exist"
return
diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/McStasOptionsConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/McStasOptionsConfigurator.py
index 933ae97b4b..c9cf35aaa9 100644
--- a/MDANSE/Src/MDANSE/Framework/Configurators/McStasOptionsConfigurator.py
+++ b/MDANSE/Src/MDANSE/Framework/Configurators/McStasOptionsConfigurator.py
@@ -14,10 +14,10 @@
# along with this program. If not, see .
#
-import os
import tempfile
import time
from typing import Dict, Any
+from pathlib import Path
from MDANSE import PLATFORM
from MDANSE.Framework.Configurators.IConfigurator import IConfigurator
@@ -50,10 +50,10 @@ class McStasOptionsConfigurator(IConfigurator):
_default = {
"ncount": 10000,
- "dir": os.path.join(
- tempfile.gettempdir(),
- "mcstas_output",
- time.strftime("%d.%m.%Y-%H:%M:%S", time.localtime()),
+ "dir": (
+ Path(tempfile.gettempdir())
+ / "mcstas_output"
+ / time.strftime("%d.%m.%Y-%H:%M:%S", time.localtime())
),
}
@@ -81,12 +81,12 @@ def configure(self, value):
for k, v in list(options.items()):
if k == "dir":
# If the output directory already exists, defines a 'unique' output directory name because otherwise McStas throws.
- if os.path.exists(v):
+ if Path(v).exists():
v = self._default["dir"]
- self["mcstas_output_directory"] = v
+ self["mcstas_output_directory"] = Path(v)
tmp.append("--%s=%s" % (k, v))
- dirname = os.path.dirname(self["mcstas_output_directory"])
+ dirname = self["mcstas_output_directory"].parent
try:
PLATFORM.create_directory(dirname)
diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/MultiInputFileConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/MultiInputFileConfigurator.py
index 59497623fb..bd327045ab 100644
--- a/MDANSE/Src/MDANSE/Framework/Configurators/MultiInputFileConfigurator.py
+++ b/MDANSE/Src/MDANSE/Framework/Configurators/MultiInputFileConfigurator.py
@@ -14,7 +14,6 @@
# along with this program. If not, see .
#
import ast
-import os
from typing import Union
from MDANSE import PLATFORM
@@ -73,7 +72,7 @@ def configure(self, setting: Union[str, list]):
none_exist = []
for value in values:
- if not os.path.isfile(value):
+ if not value.is_file():
none_exist.append(value)
if none_exist:
diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/OutputDirectoryConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/OutputDirectoryConfigurator.py
index 62f74c32e4..00bb8b7e91 100644
--- a/MDANSE/Src/MDANSE/Framework/Configurators/OutputDirectoryConfigurator.py
+++ b/MDANSE/Src/MDANSE/Framework/Configurators/OutputDirectoryConfigurator.py
@@ -55,7 +55,7 @@ def configure(self, value):
value = PLATFORM.get_path(value)
if self._new:
- if os.path.exists(value):
+ if value.exists():
self.error_status = "the output directory must not exist"
return
diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/OutputFilesConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/OutputFilesConfigurator.py
index 194b4c555b..145ab6e294 100644
--- a/MDANSE/Src/MDANSE/Framework/Configurators/OutputFilesConfigurator.py
+++ b/MDANSE/Src/MDANSE/Framework/Configurators/OutputFilesConfigurator.py
@@ -14,8 +14,7 @@
# along with this program. If not, see .
#
-import os
-from pathlib import PurePath
+from pathlib import Path
from MDANSE import PLATFORM
from MDANSE.Framework.Configurators.IConfigurator import IConfigurator
@@ -66,6 +65,7 @@ def configure(self, value):
self._original_input = value
root, formats, logs = value
+ root = Path(root)
if logs not in self.log_options:
self.error_status = "log level option not recognised"
@@ -96,23 +96,18 @@ def configure(self, value):
self["root"] = root
self["formats"] = formats
- self["files"] = []
- for extension in [IFormat.create(f).extension for f in formats]:
- if extension in root[-len(extension) :]:
- self["files"].append(root)
- else:
- self["files"].append(root + extension)
+ self["files"] = [
+ root if root.suffix == ext else root.with_suffix(root.suffix + ext)
+ for ext in (IFormat.create(f).extension for f in formats)
+ ]
for file in self["files"]:
- if PurePath(os.path.abspath(file)) in self._forbidden_files:
+ if file.absolute() in self._forbidden_files:
self.error_status = f"File {file} is either open or being written into. Please pick another name."
return
self["value"] = self["files"]
self["log_level"] = logs
- if logs == "no logs":
- self["write_logs"] = False
- else:
- self["write_logs"] = True
+ self["write_logs"] = logs != "no logs"
self.error_status = "OK"
@property
@@ -137,7 +132,7 @@ def get_information(self):
info = ["Input files:\n"]
for f in self["files"]:
- info.append(f)
+ info.append(str(f))
info.append("\n")
return "".join(info)
diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/OutputStructureConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/OutputStructureConfigurator.py
index 24c7b16a71..c55f0d1e39 100644
--- a/MDANSE/Src/MDANSE/Framework/Configurators/OutputStructureConfigurator.py
+++ b/MDANSE/Src/MDANSE/Framework/Configurators/OutputStructureConfigurator.py
@@ -14,8 +14,7 @@
# along with this program. If not, see .
#
-import os
-from pathlib import PurePath
+from pathlib import Path
from ase.io.formats import ioformats
@@ -65,6 +64,7 @@ def configure(self, value):
self._original_input = value
root, format, logs = value
+ root = Path(root)
if logs not in self.log_options:
self.error_status = "log level option not recognised"
@@ -85,14 +85,11 @@ def configure(self, value):
self["root"] = root
self["format"] = format
self["file"] = root
- if PurePath(os.path.abspath(self["file"])) in self._forbidden_files:
+ if self["file"].absolute() in self._forbidden_files:
self.error_status = f"File {self['file']} is either open or being written into. Please pick another name."
return
self["log_level"] = logs
- if logs == "no logs":
- self["write_logs"] = False
- else:
- self["write_logs"] = True
+ self["write_logs"] = logs != "no logs"
self["value"] = self["file"]
self.error_status = "OK"
diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/OutputTrajectoryConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/OutputTrajectoryConfigurator.py
index c2bec2e22a..e92988de9e 100644
--- a/MDANSE/Src/MDANSE/Framework/Configurators/OutputTrajectoryConfigurator.py
+++ b/MDANSE/Src/MDANSE/Framework/Configurators/OutputTrajectoryConfigurator.py
@@ -14,8 +14,7 @@
# along with this program. If not, see .
#
-import os
-from pathlib import PurePath
+from pathlib import Path
import numpy as np
@@ -62,6 +61,7 @@ def configure(self, value: tuple):
self._original_input = value
root, dtype, chunk_size, compression, logs = value
+ root = Path(root)
if logs not in self.log_options:
self.error_status = "log level option not recognised"
@@ -93,20 +93,19 @@ def configure(self, value: tuple):
self["format"] = self._format
self["extension"] = IFormat.create(self._format).extension
temp_name = root
- if not self["extension"] in temp_name[-5:]: # capture most extension lengths
- temp_name += self["extension"]
+ if self["extension"] != root.suffix: # capture most extension lengths
+ temp_name = temp_name.with_suffix(temp_name.suffix + self["extension"])
self["file"] = temp_name
- if PurePath(os.path.abspath(self["file"])) in self._forbidden_files:
+
+ if self["file"].absolute() in self._forbidden_files:
self.error_status = f"File {self['file']} is either open or being written into. Please pick another name."
return
+
self["dtype"] = self._dtype
self["compression"] = self._compression
self["chunk_size"] = self._chunk_limit
self["log_level"] = logs
- if logs == "no logs":
- self["write_logs"] = False
- else:
- self["write_logs"] = True
+ self["write_logs"] = logs != "no logs"
self.error_status = "OK"
@property
diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/SingleOutputFileConfigurator.py b/MDANSE/Src/MDANSE/Framework/Configurators/SingleOutputFileConfigurator.py
index 872c7a5708..7e26065e36 100644
--- a/MDANSE/Src/MDANSE/Framework/Configurators/SingleOutputFileConfigurator.py
+++ b/MDANSE/Src/MDANSE/Framework/Configurators/SingleOutputFileConfigurator.py
@@ -13,7 +13,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import os
+
+from typing import Tuple
+from pathlib import Path
+
from MDANSE import PLATFORM
from MDANSE.Framework.Configurators.IConfigurator import IConfigurator
from MDANSE.Framework.Formats.IFormat import IFormat
@@ -49,23 +52,25 @@ def __init__(self, name, format=None, **kwargs):
format if format is not None else SingleOutputFileConfigurator._default[-1]
)
- def configure(self, value):
+ def configure(self, value: Tuple[str, str]):
"""
Configure a set of output files for an analysis.
- :param value: the output files specifications. Must be a 3-tuple whose 1st element \
- is the output directory, 2nd element the basename and 3rd element a list of file formats.
- :type value: 3-tuple
+ :param value: the output files specifications.
+ Must be a 2-tuple whose 1st element is
+ the file and 2nd element a list of file formats.
+ :type value: 2-tuple
"""
self._original_input = value
root, format = value
+ root = Path(root)
if not root:
self.error_status = "empty root name for the output file."
return
- dirname = os.path.dirname(root)
+ dirname = root.parent
try:
PLATFORM.create_directory(dirname)
@@ -91,8 +96,8 @@ def configure(self, value):
self["format"] = format
self["extension"] = IFormat.create(format).extension
temp_name = root
- if not self["extension"] in temp_name[-5:]: # capture most extension lengths
- temp_name += self["extension"]
+ if self["extension"] != root.suffix:
+ temp_name = temp_name.with_suffix(temp_name.suffix + self["extension"])
self["file"] = temp_name
self.error_status = "OK"
diff --git a/MDANSE/Src/MDANSE/Framework/Configurators/__init__.py b/MDANSE/Src/MDANSE/Framework/Configurators/__init__.py
index e36d77d78c..aacbe6b787 100644
--- a/MDANSE/Src/MDANSE/Framework/Configurators/__init__.py
+++ b/MDANSE/Src/MDANSE/Framework/Configurators/__init__.py
@@ -13,23 +13,17 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import glob
import importlib
-import os
+from pathlib import Path
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module("." + name, "MDANSE.Framework.Configurators")
except ModuleNotFoundError:
diff --git a/MDANSE/Src/MDANSE/Framework/Converters/Gromacs.py b/MDANSE/Src/MDANSE/Framework/Converters/Gromacs.py
index 800b8d7dec..a01cac5320 100644
--- a/MDANSE/Src/MDANSE/Framework/Converters/Gromacs.py
+++ b/MDANSE/Src/MDANSE/Framework/Converters/Gromacs.py
@@ -15,6 +15,7 @@
#
import collections
+from pathlib import Path
import numpy as np
from mdtraj.formats.xtc import XTCTrajectoryFile
@@ -77,16 +78,13 @@ def initialize(self):
data_to_be_written = ["configuration", "time"]
+ filename = Path(self.configuration["xtc_file"]["filename"])
# Create XTC or TRR object depending on which kind of trajectory was loaded
- if self.configuration["xtc_file"]["filename"][-4:] == ".xtc":
- self._xdr_file = XTCTrajectoryFile(
- self.configuration["xtc_file"]["filename"], "r"
- )
+ if filename.suffix == ".xtc":
+ self._xdr_file = XTCTrajectoryFile(bytes(filename), "r")
self._xtc = True
- elif self.configuration["xtc_file"]["filename"][-4:] == ".trr":
- self._xdr_file = TRRTrajectoryFile(
- self.configuration["xtc_file"]["filename"], "r"
- )
+ elif filename.suffix == ".trr":
+ self._xdr_file = TRRTrajectoryFile(bytes(filename), "r")
self._xtc = False
# Extract information about whether velocities and forces are present in the TRR file
diff --git a/MDANSE/Src/MDANSE/Framework/Converters/__init__.py b/MDANSE/Src/MDANSE/Framework/Converters/__init__.py
index a614730227..54a12bba0e 100644
--- a/MDANSE/Src/MDANSE/Framework/Converters/__init__.py
+++ b/MDANSE/Src/MDANSE/Framework/Converters/__init__.py
@@ -13,25 +13,18 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import glob
import importlib
-import os
-
+from pathlib import Path
from MDANSE.MLogging import LOG
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module("." + name, "MDANSE.Framework.Converters")
except ModuleNotFoundError:
diff --git a/MDANSE/Src/MDANSE/Framework/Formats/HDFFormat.py b/MDANSE/Src/MDANSE/Framework/Formats/HDFFormat.py
index 7daf059098..1289454986 100644
--- a/MDANSE/Src/MDANSE/Framework/Formats/HDFFormat.py
+++ b/MDANSE/Src/MDANSE/Framework/Formats/HDFFormat.py
@@ -13,8 +13,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import os
-from typing import TYPE_CHECKING, Dict
+from pathlib import Path
+from typing import TYPE_CHECKING, Dict, Union
from importlib import metadata
import h5py
@@ -46,7 +46,7 @@ class HDFFormat(IFormat):
@classmethod
def write(
cls,
- filename: str,
+ filename: Union[Path, str],
data: Dict[str, "IOutputVariable"],
header: str = "",
run_instance: "IJob" = None,
@@ -67,12 +67,10 @@ def write(
"""
string_dt = h5py.special_dtype(vlen=str)
- filename = os.path.splitext(filename)[0]
-
- filename = "%s%s" % (filename, extension)
+ filename = Path(filename).with_suffix(extension)
# The HDF output file is opened for writing.
- PLATFORM.create_directory(os.path.dirname(filename))
+ PLATFORM.create_directory(filename.parent)
outputFile = h5py.File(filename, "w")
if header:
diff --git a/MDANSE/Src/MDANSE/Framework/Formats/SVGFormat.py b/MDANSE/Src/MDANSE/Framework/Formats/SVGFormat.py
index 6b2a187615..e7acd8170c 100644
--- a/MDANSE/Src/MDANSE/Framework/Formats/SVGFormat.py
+++ b/MDANSE/Src/MDANSE/Framework/Formats/SVGFormat.py
@@ -14,10 +14,11 @@
# along with this program. If not, see .
#
-import os
import re
import io
import tarfile
+from pathlib import Path
+from typing import Union
import numpy as np
@@ -45,7 +46,7 @@ class SVGFormat(IFormat):
extensions = [".svg"]
@classmethod
- def write(cls, filename, data, header=""):
+ def write(cls, filename: Union[Path, str], data, header=""):
"""
Write a set of output variables into a set of SVG files.
@@ -59,8 +60,7 @@ def write(cls, filename, data, header=""):
:type header: str
"""
- filename = os.path.splitext(filename)[0]
- filename = "%s.tar" % filename
+ filename = Path(filename).with_suffix(".tar")
tf = tarfile.open(filename, "w")
@@ -79,9 +79,7 @@ def write(cls, filename, data, header=""):
pl = Poly(list(zip(axis, var)), stroke="blue")
- svgfilename = os.path.join(
- os.path.dirname(filename), "%s%s" % (var.varname, cls.extensions[0])
- )
+ svgfilename = filename.parent / f"{var.varname}{cls.extensions[0]}"
Frame(
min(axis),
@@ -95,7 +93,7 @@ def write(cls, filename, data, header=""):
tf.add(svgfilename, arcname="%s%s" % (var.varname, cls.extensions[0]))
- os.remove(svgfilename)
+ svgfilename.unlink()
if header:
tempStr = io.StringIO()
diff --git a/MDANSE/Src/MDANSE/Framework/Formats/TextFormat.py b/MDANSE/Src/MDANSE/Framework/Formats/TextFormat.py
index 8a62a82751..e355f733c5 100644
--- a/MDANSE/Src/MDANSE/Framework/Formats/TextFormat.py
+++ b/MDANSE/Src/MDANSE/Framework/Formats/TextFormat.py
@@ -14,12 +14,12 @@
# along with this program. If not, see .
#
-import os
import io
import tarfile
import codecs
import time
-from typing import TYPE_CHECKING
+from pathlib import Path
+from typing import TYPE_CHECKING, Union
from importlib import metadata
import numpy as np
@@ -47,7 +47,13 @@ class TextFormat(IFormat):
extensions = [".dat", ".txt"]
@classmethod
- def write(cls, filename, data, header: str = "", run_instance: "IJob" = None):
+ def write(
+ cls,
+ filename: Union[Path, str],
+ data,
+ header: str = "",
+ run_instance: "IJob" = None,
+ ):
"""
Write a set of output variables into a set of Text files.
@@ -61,10 +67,10 @@ def write(cls, filename, data, header: str = "", run_instance: "IJob" = None):
:type header: str
"""
- filename = os.path.splitext(filename)[0]
- filename = "%s_text.tar" % filename
+ filename = Path(filename)
+ filename = filename.parent / (filename.stem + "_text.tar")
- PLATFORM.create_directory(os.path.dirname(filename))
+ PLATFORM.create_directory(filename.parent)
tf = tarfile.open(filename, "w")
if header:
diff --git a/MDANSE/Src/MDANSE/Framework/Formats/__init__.py b/MDANSE/Src/MDANSE/Framework/Formats/__init__.py
index 7739e3979c..3b55689e9c 100644
--- a/MDANSE/Src/MDANSE/Framework/Formats/__init__.py
+++ b/MDANSE/Src/MDANSE/Framework/Formats/__init__.py
@@ -13,23 +13,17 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import glob
import importlib
-import os
+from pathlib import Path
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module("." + name, "MDANSE.Framework.Formats")
except ModuleNotFoundError:
diff --git a/MDANSE/Src/MDANSE/Framework/Handlers/__init__.py b/MDANSE/Src/MDANSE/Framework/Handlers/__init__.py
index 68d24466c0..8247aa0c81 100644
--- a/MDANSE/Src/MDANSE/Framework/Handlers/__init__.py
+++ b/MDANSE/Src/MDANSE/Framework/Handlers/__init__.py
@@ -13,23 +13,17 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import glob
import importlib
-import os
+from pathlib import Path
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module("." + name, "MDANSE.Framework.Handlers")
except ModuleNotFoundError:
diff --git a/MDANSE/Src/MDANSE/Framework/InputData/InputFileData.py b/MDANSE/Src/MDANSE/Framework/InputData/InputFileData.py
index 132978c99e..ecafeede2e 100644
--- a/MDANSE/Src/MDANSE/Framework/InputData/InputFileData.py
+++ b/MDANSE/Src/MDANSE/Framework/InputData/InputFileData.py
@@ -15,7 +15,7 @@
#
import abc
-import os
+from pathlib import Path
from MDANSE.Framework.InputData.IInputData import IInputData
@@ -23,9 +23,9 @@
class InputFileData(IInputData):
def __init__(self, filename, load=True):
IInputData.__init__(self, filename)
-
- self._basename = os.path.basename(filename)
- self._dirname = os.path.dirname(filename)
+ filename = Path(filename)
+ self._basename = filename.name
+ self._dirname = filename.parent
if load:
self.load()
diff --git a/MDANSE/Src/MDANSE/Framework/InputData/__init__.py b/MDANSE/Src/MDANSE/Framework/InputData/__init__.py
index b97a9c724f..c6ad9aba40 100644
--- a/MDANSE/Src/MDANSE/Framework/InputData/__init__.py
+++ b/MDANSE/Src/MDANSE/Framework/InputData/__init__.py
@@ -13,23 +13,17 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import glob
import importlib
-import os
+from pathlib import Path
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module("." + name, "MDANSE.Framework.InputData")
except ModuleNotFoundError:
diff --git a/MDANSE/Src/MDANSE/Framework/InstrumentResolutions/__init__.py b/MDANSE/Src/MDANSE/Framework/InstrumentResolutions/__init__.py
index 0003dbb3b2..611b7484a7 100644
--- a/MDANSE/Src/MDANSE/Framework/InstrumentResolutions/__init__.py
+++ b/MDANSE/Src/MDANSE/Framework/InstrumentResolutions/__init__.py
@@ -13,23 +13,17 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import glob
import importlib
-import os
+from pathlib import Path
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module(
"." + name, "MDANSE.Framework.InstrumentResolutions"
diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/AverageStructure.py b/MDANSE/Src/MDANSE/Framework/Jobs/AverageStructure.py
index d91602e680..f30e5ebdcd 100644
--- a/MDANSE/Src/MDANSE/Framework/Jobs/AverageStructure.py
+++ b/MDANSE/Src/MDANSE/Framework/Jobs/AverageStructure.py
@@ -13,8 +13,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import os
import collections
+from pathlib import Path
import numpy as np
from ase.io import write as ase_write
@@ -184,7 +184,7 @@ def finalize(self):
self._ase_atoms.set_scaled_positions(temp - correction)
PLATFORM.create_directory(
- os.path.dirname(self.configuration["output_files"]["file"])
+ Path(self.configuration["output_files"]["file"]).parent
)
ase_write(
self.configuration["output_files"]["file"],
diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/IJob.py b/MDANSE/Src/MDANSE/Framework/Jobs/IJob.py
index 447157bc9e..f41191ce24 100644
--- a/MDANSE/Src/MDANSE/Framework/Jobs/IJob.py
+++ b/MDANSE/Src/MDANSE/Framework/Jobs/IJob.py
@@ -18,7 +18,6 @@
from logging.handlers import QueueHandler, QueueListener
import abc
-import glob
import os
import multiprocessing
import queue
@@ -28,6 +27,7 @@
import time
import sys
import traceback
+from pathlib import Path
from MDANSE import PLATFORM
from MDANSE.Core.Error import Error
@@ -107,8 +107,7 @@ def define_unique_name():
# The list of the registered jobs.
registeredJobs = [
- os.path.basename(f)
- for f in glob.glob(os.path.join(PLATFORM.temporary_files_directory(), "*"))
+ f.name for f in PLATFORM.temporary_files_directory().glob("*")
]
while True:
@@ -164,7 +163,7 @@ def initialize(self):
"output_files" in self.configuration
and self.configuration["output_files"]["write_logs"]
):
- log_filename = self.configuration["output_files"]["root"] + ".log"
+ log_filename = str(self.configuration["output_files"]["root"]) + ".log"
self.add_log_file_handler(
log_filename, self.configuration["output_files"]["log_level"]
)
@@ -430,13 +429,13 @@ def save_template(cls, shortname, classname):
"A job with %r name is already stored in the registry" % shortname
)
- templateFile = os.path.join(PLATFORM.macros_directory(), "%s.py" % classname)
+ templateFile = PLATFORM.macros_directory() / f"{classname}.py"
try:
- f = open(templateFile, "w")
+ with templateFile.open("w") as f:
- f.write(
- '''import collections
+ f.write(
+ '''import collections
from MDANSE.Framework.Jobs.IJob import IJob
@@ -444,13 +443,13 @@ class %(classname)s(IJob):
"""
You should enter the description of your job here ...
"""
-
+
# You should enter the label under which your job will be viewed from the gui.
label = %(label)r
# You should enter the category under which your job will be references.
category = ('My jobs',)
-
+
ancestor = ["hdf_trajectory"]
# You should enter the configuration of your job here
@@ -459,7 +458,7 @@ class %(classname)s(IJob):
settings['trajectory']=('hdf_trajectory',{})
settings['frames']=('frames', {"dependencies":{'trajectory':'trajectory'}})
settings['output_files']=('output_files', {"formats":["HDFFormat","netcdf","TextFormat"]})
-
+
def initialize(self):
"""
Initialize the input parameters and analysis self variables
@@ -468,7 +467,7 @@ def initialize(self):
# Compulsory. You must enter the number of steps of your job.
# Here for example the number of selected frames
self.numberOfSteps = self.configuration['frames']['number']
-
+
# Create an output data for the selected frames.
self._outputData.add("time", "LineOutputVariable", self.configuration['frames']['time'], units='ps')
@@ -477,40 +476,38 @@ def run_step(self, index):
"""
Runs a single step of the job.
"""
-
+
return index, None
-
-
+
+
def combine(self, index, x):
"""
Synchronize the output of each individual run_step output.
- """
-
+ """
+
def finalize(self):
"""
Finalizes the job (e.g. averaging the total term, output files creations ...).
- """
+ """
# The output data are written
self._outputData.write(self.configuration['output_files']['root'], self.configuration['output_files']['formats'], self._info,
self.output_configuration())
-
+
# The trajectory is closed
- self.configuration['trajectory']['instance'].close()
-
-'''
- % {
- "classname": classname,
- "label": "label of the class",
- "shortname": shortname,
- }
- )
+ self.configuration['trajectory']['instance'].close()
+
+ '''
+ % {
+ "classname": classname,
+ "label": "label of the class",
+ "shortname": shortname,
+ }
+ )
except IOError:
return None
- else:
- f.close()
- return templateFile
+ return templateFile
def add_log_file_handler(self, filename: str, level: str) -> None:
"""Adds a file handle which is used to write the jobs logs.
@@ -523,8 +520,8 @@ def add_log_file_handler(self, filename: str, level: str) -> None:
The log level.
"""
self._log_filename = filename
- PLATFORM.create_directory(os.path.dirname(filename))
- fh = FileHandler(filename, mode="w")
+ PLATFORM.create_directory(Path(self._log_filename).parent)
+ fh = FileHandler(self._log_filename, mode="w")
# set the name so that we can track it and then close it later,
# tracking the fh by storing it in this object causes issues
# with multiprocessing jobs
diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/McStasVirtualInstrument.py b/MDANSE/Src/MDANSE/Framework/Jobs/McStasVirtualInstrument.py
index 8ef6dc5fdd..89fdf873d4 100644
--- a/MDANSE/Src/MDANSE/Framework/Jobs/McStasVirtualInstrument.py
+++ b/MDANSE/Src/MDANSE/Framework/Jobs/McStasVirtualInstrument.py
@@ -14,11 +14,12 @@
# along with this program. If not, see .
#
import collections
-import os
import shutil
import subprocess
import tempfile
import io
+from pathlib import Path
+from typing import Union
import numpy as np
@@ -117,6 +118,13 @@ class McStasVirtualInstrument(IJob):
{"formats": ["MDAFormat", "TextFormat"]},
)
+ @property
+ def mcstas_output_dir(self) -> Path:
+ """
+ Output directory as path.
+ """
+ return Path(self.configuration["options"]["mcstas_output_directory"])
+
def initialize(self):
"""
Initialize the input parameters and analysis self variables
@@ -284,13 +292,8 @@ def run_step(self, index):
if "ERROR" in line.decode(encoding="utf-8"):
raise McStasError("An error occured during McStas run: %s" % out)
- with open(
- os.path.join(
- self.configuration["options"]["mcstas_output_directory"],
- "mcstas_mdanse.mvi",
- ),
- "w",
- ) as f:
+ out_file = self.mcstas_output_dir / "mcstas_mdanse.mvi"
+ with out_file.open("w") as f:
f.write(out.decode(encoding="utf-8"))
return index, None
@@ -310,14 +313,8 @@ def finalize(self):
"""
# Rename and move to the result dir the SQW file input
- for typ, fname in list(self.outFile.items()):
- shutil.move(
- fname,
- os.path.join(
- self.configuration["options"]["mcstas_output_directory"],
- typ + ".sqw",
- ),
- )
+ for typ, fname in self.outFile.items():
+ shutil.move(fname, self.mcstas_output_dir / f"{typ}.sqw")
# Convert McStas output files into NetCDF format
self.convert(self.configuration["options"]["mcstas_output_directory"])
@@ -347,20 +344,21 @@ def unique(self, key, d, value=None):
i += 1
return key
- def convert(self, sim_dir):
+ def convert(self, sim_dir: Union[Path, str]):
"""
Convert McStas data set to netCDF File Format
"""
+ sim_dir = Path(sim_dir)
typique_sim_fnames = ["mccode.sim", "mcstas.sim"]
- sim_file = ""
+
for sim_fname in typique_sim_fnames:
- sim_file = os.path.join(sim_dir, sim_fname)
- if os.path.isfile(sim_file):
+ sim_file = sim_dir / sim_fname
+ if sim_file.is_file():
break
if not sim_file:
- raise Exception("Dataset " + sim_file + " does not exist!")
+ raise Exception(f"Dataset {sim_file} does not exist!")
isBegin = lambda line: line.strip().startswith("begin")
isCompFilename = lambda line: line.strip().startswith("filename:")
@@ -387,7 +385,7 @@ def convert(self, sim_dir):
Scanfile = list(filter(isFilename, open(sim_file).readlines()))
Scanfile = Scanfile[0].split(": ")
- Scanfile = os.path.join(sim_dir, Scanfile[1].strip())
+ Scanfile = sim_dir / Scanfile[1].strip()
# Proceed to load scan datafile
FS = self.read_monitor(Scanfile)
L = (len(FS["variables"].split()) - 1) / 2
@@ -402,7 +400,7 @@ def convert(self, sim_dir):
for j in range(0, L):
MonFile = MonFiles[j].split(":")
MonFile = MonFile[1].strip()
- MonFile = os.path.join(sim_dir, MonFile)
+ MonFile = sim_dir / MonFile
FS = self.read_monitor(MonFile)
FSlist[len(FSlist) :] = [FS]
FSlist[j] = self.save_single(FS)
diff --git a/MDANSE/Src/MDANSE/Framework/Jobs/__init__.py b/MDANSE/Src/MDANSE/Framework/Jobs/__init__.py
index 807908a128..1a84524377 100644
--- a/MDANSE/Src/MDANSE/Framework/Jobs/__init__.py
+++ b/MDANSE/Src/MDANSE/Framework/Jobs/__init__.py
@@ -13,25 +13,18 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import glob
import importlib
-import os
-
+from pathlib import Path
from MDANSE.MLogging import LOG
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module("." + name, "MDANSE.Framework.Jobs")
except ModuleNotFoundError:
diff --git a/MDANSE/Src/MDANSE/Framework/OutputVariables/__init__.py b/MDANSE/Src/MDANSE/Framework/OutputVariables/__init__.py
index c0eab43316..d896026c9f 100644
--- a/MDANSE/Src/MDANSE/Framework/OutputVariables/__init__.py
+++ b/MDANSE/Src/MDANSE/Framework/OutputVariables/__init__.py
@@ -13,23 +13,17 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import glob
import importlib
-import os
+from pathlib import Path
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module(
"." + name, "MDANSE.Framework.OutputVariables"
diff --git a/MDANSE/Src/MDANSE/Framework/Projectors/__init__.py b/MDANSE/Src/MDANSE/Framework/Projectors/__init__.py
index f057eb2ad6..b13afdf3c8 100644
--- a/MDANSE/Src/MDANSE/Framework/Projectors/__init__.py
+++ b/MDANSE/Src/MDANSE/Framework/Projectors/__init__.py
@@ -13,23 +13,18 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import glob
+
import importlib
-import os
+from pathlib import Path
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module("." + name, "MDANSE.Framework.Projectors")
except ModuleNotFoundError:
diff --git a/MDANSE/Src/MDANSE/Framework/QVectors/__init__.py b/MDANSE/Src/MDANSE/Framework/QVectors/__init__.py
index 8ca3a8bf2e..1c2ce1ee0a 100644
--- a/MDANSE/Src/MDANSE/Framework/QVectors/__init__.py
+++ b/MDANSE/Src/MDANSE/Framework/QVectors/__init__.py
@@ -13,23 +13,18 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import glob
+
import importlib
-import os
+from pathlib import Path
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module("." + name, "MDANSE.Framework.QVectors")
except ModuleNotFoundError:
diff --git a/MDANSE/Src/MDANSE/Framework/Session/CurrentSession.py b/MDANSE/Src/MDANSE/Framework/Session/CurrentSession.py
index 844b7eb8a3..74619200a0 100644
--- a/MDANSE/Src/MDANSE/Framework/Session/CurrentSession.py
+++ b/MDANSE/Src/MDANSE/Framework/Session/CurrentSession.py
@@ -14,9 +14,8 @@
# along with this program. If not, see .
#
-import os
-from os.path import expanduser
import json
+from pathlib import Path
from abc import ABC, abstractmethod
@@ -35,7 +34,7 @@ def load_session(self, fname: str):
class SessionSettings(AbstractSession):
def __init__(self):
super().__init__()
- self.main_path = os.path.abspath(".")
+ self.main_path = Path(".").absolute()
def create_structured_project(self):
self.relative_paths = {
@@ -47,7 +46,7 @@ def create_structured_project(self):
class CurrentSession:
def __init__(self, fname=None):
- self.settings_dir = os.path.join(expanduser("~"), ".MDANSE")
+ self.settings_dir = Path("~").expanduser() / ".MDANSE"
if fname is not None:
self.loadSettings(fname)
diff --git a/MDANSE/Src/MDANSE/Framework/Units.py b/MDANSE/Src/MDANSE/Framework/Units.py
index 53e7addcdf..b8dab49fe6 100644
--- a/MDANSE/Src/MDANSE/Framework/Units.py
+++ b/MDANSE/Src/MDANSE/Framework/Units.py
@@ -16,7 +16,6 @@
import copy
import math
import numbers
-import os
import json
@@ -694,11 +693,11 @@ def default(self, obj):
class UnitsManager(metaclass=Singleton):
_UNITS = {}
- _DEFAULT_DATABASE = os.path.join(
- PLATFORM.base_directory(), "MDANSE", "Framework", "units.json"
+ _DEFAULT_DATABASE = (
+ PLATFORM.base_directory() / "MDANSE" / "Framework" / "units.json"
)
- _USER_DATABASE = os.path.join(PLATFORM.application_directory(), "units.json")
+ _USER_DATABASE = PLATFORM.application_directory() / "units.json"
def __init__(self):
self.load()
diff --git a/MDANSE/Src/MDANSE/Framework/UserDefinitionStore.py b/MDANSE/Src/MDANSE/Framework/UserDefinitionStore.py
index 15fc4f620c..49c9cbee72 100644
--- a/MDANSE/Src/MDANSE/Framework/UserDefinitionStore.py
+++ b/MDANSE/Src/MDANSE/Framework/UserDefinitionStore.py
@@ -14,7 +14,6 @@
# along with this program. If not, see .
#
import pickle
-import os
from MDANSE import PLATFORM
from MDANSE.Core.Error import Error
@@ -36,7 +35,7 @@ class UserDefinitionStore(object, metaclass=Singleton):
user definitions are loaded when MDANSE starts through a cPickle file that will store these definitions.
"""
- UD_PATH = os.path.join(PLATFORM.application_directory(), "user_definitions_md3.ud")
+ UD_PATH = PLATFORM.application_directory() / "user_definitions_md3.ud"
def __init__(self):
self._definitions = {}
@@ -52,12 +51,12 @@ def load(self):
Load the user definitions.
"""
- if not os.path.exists(UserDefinitionStore.UD_PATH):
+ if not UserDefinitionStore.UD_PATH.exists():
return
# Try to open the UD file.
try:
- f = open(UserDefinitionStore.UD_PATH, "rb")
+ f = UserDefinitionStore.UD_PATH.open("rb")
UD = pickle.load(f)
# If for whatever reason the pickle file loading failed do not even try to restore it
diff --git a/MDANSE/Src/MDANSE/IO/TextFile.py b/MDANSE/Src/MDANSE/IO/TextFile.py
index 34abdcdbb9..6892a2d0d9 100644
--- a/MDANSE/Src/MDANSE/IO/TextFile.py
+++ b/MDANSE/Src/MDANSE/IO/TextFile.py
@@ -18,12 +18,13 @@
Text files with line iteration and transparent compression
"""
-import os, string, sys
+import os
+import sys
+from pathlib import Path
# Use the gzip module for Python version 1.5.2 or higher
-gzip = None
try:
- _version = [int(c) for c in string.split(string.split(sys.version)[0], ".")]
+ _version = [int(c) for c in sys.version.split()[0].split(".")]
if _version >= [1, 5, 2]:
try:
@@ -31,7 +32,7 @@
except ImportError:
gzip = None
except:
- pass
+ gzip = None
class TextFile:
@@ -63,57 +64,67 @@ def __init__(self, filename, mode="r"):
if filename.find(":/") > 1: # URL
if mode != "r":
raise IOError("can't write to a URL")
- import urllib.request, urllib.parse, urllib.error
+ import urllib
self.file = urllib.request.urlopen(filename)
else:
- filename = os.path.expanduser(filename)
+ filename = Path(filename).expanduser()
if mode in ["r", "rt"]:
- if not os.path.exists(filename):
- raise IOError((2, "No such file or directory: " + filename))
- if filename[-2:] == ".Z":
- self.file = os.popen("uncompress -c " + filename, mode)
- elif filename[-3:] == ".gz":
+ if not filename.exists():
+ raise IOError((2, f"No such file or directory: {filename}"))
+
+ if filename.suffix == ".Z":
+ self.file = os.popen(f"uncompress -c {filename}", mode)
+
+ elif filename.suffix == ".gz":
if gzip is None:
- self.file = os.popen("gunzip -c " + filename, mode)
+ self.file = os.popen(f"gunzip -c {filename}", mode)
else:
self.file = gzip.GzipFile(filename, "rb")
- elif filename[-4:] == ".bz2":
- self.file = os.popen("bzip2 -dc " + filename, mode)
+
+ elif filename.suffix == ".bz2":
+ self.file = os.popen(f"bzip2 -dc {filename}", mode)
+
else:
try:
self.file = open(filename, mode)
except IOError as details:
- if type(details) == type(()):
+ if isinstance(details, tuple):
details = details + (filename,)
raise IOError(details)
+
elif mode == "w":
- if filename[-2:] == ".Z":
- self.file = os.popen("compress > " + filename, mode)
- elif filename[-3:] == ".gz":
+ if filename.suffix == ".Z":
+ self.file = os.popen(f"compress > {filename}", mode)
+
+ elif filename.suffix == ".gz":
if gzip is None:
- self.file = os.popen("gzip > " + filename, mode)
+ self.file = os.popen(f"gzip > {filename}", mode)
else:
self.file = gzip.GzipFile(filename, "wb")
- elif filename[-4:] == ".bz2":
- self.file = os.popen("bzip2 > " + filename, mode)
+
+ elif filename.suffix == ".bz2":
+ self.file = os.popen(f"bzip2 > {filename}", mode)
+
else:
try:
self.file = open(filename, mode)
except IOError as details:
- if type(details) == type(()):
+ if isinstance(details, tuple):
details = details + (filename,)
raise IOError(details)
+
elif mode == "a":
- if filename[-2:] == ".Z":
+ if filename.suffix == ".Z":
raise IOError((0, "Can't append to .Z files"))
- elif filename[-3:] == ".gz":
+ elif filename.suffix == ".gz":
if gzip is None:
- self.file = os.popen("gzip >> " + filename, "w")
+ self.file = os.popen(f"gzip >> {filename}", "w")
else:
self.file = gzip.GzipFile(filename, "ab")
else:
self.file = open(filename, mode)
+
else:
raise IOError((0, "Illegal mode: " + repr(mode)))
diff --git a/MDANSE/Src/MDANSE/MolecularDynamics/Trajectory.py b/MDANSE/Src/MDANSE/MolecularDynamics/Trajectory.py
index 0abda4616c..9cc8a5b725 100644
--- a/MDANSE/Src/MDANSE/MolecularDynamics/Trajectory.py
+++ b/MDANSE/Src/MDANSE/MolecularDynamics/Trajectory.py
@@ -13,27 +13,23 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import os
-from ast import operator
-from typing import Collection, List, Dict, TYPE_CHECKING, Any
+import operator
+from typing import TYPE_CHECKING, Any, Collection, Dict, List, Union
if TYPE_CHECKING:
from MDANSE.Chemistry.Databases import AtomsDatabase
+
import math
+from pathlib import Path
-import numpy as np
import h5py
-
-from MDANSE.MLogging import LOG
-from MDANSE.Trajectory.MdanseTrajectory import MdanseTrajectory
-from MDANSE.Trajectory.H5MDTrajectory import H5MDTrajectory
+import numpy as np
+from MDANSE import PLATFORM
from MDANSE.Chemistry import ATOMS_DATABASE
from MDANSE.Chemistry.ChemicalSystem import ChemicalSystem
-from MDANSE.MolecularDynamics.Configuration import (
- RealConfiguration,
-)
-from MDANSE import PLATFORM
-
+from MDANSE.MolecularDynamics.Configuration import RealConfiguration
+from MDANSE.Trajectory.H5MDTrajectory import H5MDTrajectory
+from MDANSE.Trajectory.MdanseTrajectory import MdanseTrajectory
available_formats = {
"MDANSE": MdanseTrajectory,
@@ -461,7 +457,7 @@ class TrajectoryWriter:
def __init__(
self,
- h5_filename,
+ h5_filename: Union[Path, str],
chemical_system: ChemicalSystem,
n_steps,
selected_atoms=None,
@@ -482,8 +478,8 @@ def __init__(
:type selected_atoms: list of MDANSE.Chemistry.ChemicalSystem.Atom
"""
- self._h5_filename = h5_filename
- PLATFORM.create_directory(os.path.dirname(h5_filename))
+ self._h5_filename = Path(h5_filename)
+ PLATFORM.create_directory(self._h5_filename.parent)
self._h5_file = h5py.File(self._h5_filename, "w")
self._chemical_system = chemical_system
@@ -1007,6 +1003,7 @@ def read_atoms_trajectory(
if __name__ == "__main__":
+ from MDANSE.Chemistry.ChemicalEntity import Atom
from MDANSE.MolecularDynamics.Configuration import RealConfiguration
cs = ChemicalSystem()
diff --git a/MDANSE/Src/MDANSE/NeutronInstruments/Coverage/__init__.py b/MDANSE/Src/MDANSE/NeutronInstruments/Coverage/__init__.py
index 4caf3ac941..002ad78093 100644
--- a/MDANSE/Src/MDANSE/NeutronInstruments/Coverage/__init__.py
+++ b/MDANSE/Src/MDANSE/NeutronInstruments/Coverage/__init__.py
@@ -20,23 +20,17 @@
a specific instrument can access.
"""
-import glob
import importlib
-import os
+from pathlib import Path
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module(
"." + name, "MDANSE.NeutronInstruments.Coverage"
diff --git a/MDANSE/Src/MDANSE/NeutronInstruments/Method/__init__.py b/MDANSE/Src/MDANSE/NeutronInstruments/Method/__init__.py
index b326884607..30a570b634 100644
--- a/MDANSE/Src/MDANSE/NeutronInstruments/Method/__init__.py
+++ b/MDANSE/Src/MDANSE/NeutronInstruments/Method/__init__.py
@@ -26,23 +26,17 @@
direct and indirect spectrometry."""
-import glob
import importlib
-import os
+from pathlib import Path
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module(
"." + name, "MDANSE.NeutronInstruments.Method"
diff --git a/MDANSE/Src/MDANSE/NeutronInstruments/Resolution/__init__.py b/MDANSE/Src/MDANSE/NeutronInstruments/Resolution/__init__.py
index b7b0c32c4a..c01f68581c 100644
--- a/MDANSE/Src/MDANSE/NeutronInstruments/Resolution/__init__.py
+++ b/MDANSE/Src/MDANSE/NeutronInstruments/Resolution/__init__.py
@@ -23,23 +23,17 @@
resolution will depend on the source-sample and sample-detector
distances, the chopper speeds, and the Ei/Ef ratio."""
-import glob
import importlib
-import os
+from pathlib import Path
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module(
"." + name, "MDANSE.NeutronInstruments.Resolution"
diff --git a/MDANSE/Src/MDANSE/NeutronInstruments/Spectrum/__init__.py b/MDANSE/Src/MDANSE/NeutronInstruments/Spectrum/__init__.py
index a80b96f6a0..89cf30be3d 100644
--- a/MDANSE/Src/MDANSE/NeutronInstruments/Spectrum/__init__.py
+++ b/MDANSE/Src/MDANSE/NeutronInstruments/Spectrum/__init__.py
@@ -22,23 +22,17 @@
different neutron wavelength to the total observed scattering signal.
"""
-import glob
import importlib
-import os
+from pathlib import Path
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module(
"." + name, "MDANSE.NeutronInstruments.Spectrum"
diff --git a/MDANSE/Src/MDANSE/NeutronInstruments/__init__.py b/MDANSE/Src/MDANSE/NeutronInstruments/__init__.py
index 5db818526e..f25ce9821f 100644
--- a/MDANSE/Src/MDANSE/NeutronInstruments/__init__.py
+++ b/MDANSE/Src/MDANSE/NeutronInstruments/__init__.py
@@ -31,23 +31,17 @@
effects in the calculation.
"""
-import glob
import importlib
-import os
+from pathlib import Path
-current_path, _ = os.path.split(__file__)
+current_path = Path(__file__).parent
-modnames = []
-fnames = glob.glob(current_path + "/*.py")
-for fname in fnames:
- _, newname = os.path.split(fname)
- newname = newname.split(".py")[0]
- modnames.append(newname)
+modnames = (
+ fname.stem for fname in current_path.glob("*.py") if fname.stem != "__init__"
+)
globdict = globals()
for name in modnames:
- if name in ["__init__"]:
- continue
try:
tempmod = importlib.import_module("." + name, "MDANSE.NeutronInstruments")
except ModuleNotFoundError:
diff --git a/MDANSE/Src/MDANSE/Scripts/mdanse.py b/MDANSE/Src/MDANSE/Scripts/mdanse.py
index f24110110a..cdb13c6ef3 100644
--- a/MDANSE/Src/MDANSE/Scripts/mdanse.py
+++ b/MDANSE/Src/MDANSE/Scripts/mdanse.py
@@ -14,12 +14,11 @@
# along with this program. If not, see .
#
import pickle
-import glob
import optparse
-import os
import subprocess
import sys
import textwrap
+from pathlib import Path
from MDANSE.Core.Error import Error
from MDANSE import PLATFORM
@@ -118,9 +117,9 @@ def check_job(self, option, opt_str, value, parser):
basename = parser.rargs[0]
- filename = os.path.join(PLATFORM.temporary_files_directory(), basename)
+ filename = PLATFORM.temporary_files_directory() / basename
- if not os.path.exists(filename):
+ if not filename.exists():
raise CommandLineParserError("Invalid job name")
# Open the job temporary file
@@ -183,17 +182,13 @@ def display_jobs_list(self, option, opt_str, value, parser):
"Invalid number of arguments for %r option" % opt_str
)
- jobs = [
- f
- for f in glob.glob(os.path.join(PLATFORM.temporary_files_directory(), "*"))
- ]
+ jobs = PLATFORM.temporary_files_directory().glob("*")
for j in jobs:
# Open the job temporary file
try:
- f = open(j, "rb")
- info = pickle.load(f)
- f.close()
+ with j.open("rb") as f:
+ info = pickle.load(f)
# If the file could not be opened/unpickled for whatever reason, try at the next checkpoint
except:
@@ -205,7 +200,7 @@ def display_jobs_list(self, option, opt_str, value, parser):
if not isinstance(info, JobState):
continue
- LOG.info("%-20s [%s]" % (os.path.basename(j), info["state"]))
+ LOG.info("%-20s [%s]", j.stem, info["state"])
def display_trajectory_contents(self, option, opt_str, value, parser):
"""Displays trajectory contents
@@ -283,9 +278,9 @@ def run_job(self, option, opt_str, value, parser):
"Invalid number of arguments for %r option" % opt_str
)
- filename = parser.rargs[0]
+ filename = Path(parser.rargs[0])
- if not os.path.exists(filename):
+ if not filename.exists():
raise CommandLineParserError(
"The job file %r could not be executed" % filename
)
@@ -319,7 +314,7 @@ def save_job(self, option, opt_str, value, parser):
name = parser.rargs[0]
# A name for the template is built.
- filename = os.path.abspath("template_%s.py" % name.lower())
+ filename = Path(f"template_{name.lower()}.py").absolute()
# Try to save the template for the job.
try:
diff --git a/MDANSE/Src/MDANSE/Trajectory/H5MDTrajectory.py b/MDANSE/Src/MDANSE/Trajectory/H5MDTrajectory.py
index 2684e54bfe..02aa55b937 100644
--- a/MDANSE/Src/MDANSE/Trajectory/H5MDTrajectory.py
+++ b/MDANSE/Src/MDANSE/Trajectory/H5MDTrajectory.py
@@ -14,8 +14,8 @@
# along with this program. If not, see .
#
-from typing import List
-import os
+from pathlib import Path
+from typing import Union, List
import numpy as np
import h5py
@@ -42,14 +42,14 @@ class H5MDTrajectory:
H5MD files created by MDMC.
"""
- def __init__(self, h5_filename):
+ def __init__(self, h5_filename: Union[Path, str]):
"""Constructor.
:param h5_filename: the trajectory filename
:type h5_filename: str
"""
- self._h5_filename = h5_filename
+ self._h5_filename = Path(h5_filename)
self._h5_file = h5py.File(self._h5_filename, "r")
@@ -60,9 +60,7 @@ def __init__(self, h5_filename):
]
except KeyError:
chemical_elements = self._h5_file["/particles/all/species"]
- self._chemical_system = ChemicalSystem(
- os.path.splitext(os.path.basename(self._h5_filename))[0]
- )
+ self._chemical_system = ChemicalSystem(self._h5_filename.stem)
self._chemical_system.initialise_atoms(chemical_elements)
# Load all the unit cells
diff --git a/MDANSE/Src/MDANSE/Trajectory/MdanseTrajectory.py b/MDANSE/Src/MDANSE/Trajectory/MdanseTrajectory.py
index eab9d9dd8e..b2641d9b56 100644
--- a/MDANSE/Src/MDANSE/Trajectory/MdanseTrajectory.py
+++ b/MDANSE/Src/MDANSE/Trajectory/MdanseTrajectory.py
@@ -13,9 +13,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-import os
-from typing import List
-import traceback
+from pathlib import Path
+from typing import List, Union
import numpy as np
import h5py
@@ -41,28 +40,27 @@ class MdanseTrajectory:
is the original implementation of the Mdanse HDF5 format.
"""
- def __init__(self, h5_filename):
+ def __init__(self, h5_filename: Union[Path, str]):
"""Constructor.
:param h5_filename: the trajectory filename
:type h5_filename: str
"""
- self._h5_filename = h5_filename
+ self._h5_filename = Path(h5_filename)
self._h5_file = h5py.File(self._h5_filename, "r")
# Load the chemical system
- self._chemical_system = ChemicalSystem(
- os.path.splitext(os.path.basename(self._h5_filename))[0], self
- )
+ self._chemical_system = ChemicalSystem(self._h5_filename.stem, self)
self._chemical_system.load(self._h5_file)
# Load all the unit cells
self._load_unit_cells()
@classmethod
- def file_is_right(self, filename):
+ def file_is_right(self, filename: Union[Path, str]):
+ filename = Path(filename)
result = True
try:
file_object = h5py.File(filename)
@@ -70,9 +68,7 @@ def file_is_right(self, filename):
result = False
else:
try:
- temp_cs = ChemicalSystem(
- os.path.splitext(os.path.basename(filename))[0]
- )
+ temp_cs = ChemicalSystem(filename.stem)
temp_cs.load(file_object)
except Exception:
LOG.warning(
diff --git a/MDANSE/Tests/UnitTests/test_databases.py b/MDANSE/Tests/UnitTests/test_databases.py
index c508e255a6..c384d2b156 100644
--- a/MDANSE/Tests/UnitTests/test_databases.py
+++ b/MDANSE/Tests/UnitTests/test_databases.py
@@ -14,7 +14,7 @@
# along with this program. If not, see .
#
import json
-import os
+from pathlib import Path
import unittest
from unittest.mock import patch, mock_open, ANY
@@ -86,7 +86,7 @@ def test__load_default_database(self):
"atoms": {"H": {"family": "non-metal"}},
}
),
- ) as m:
+ ) as _m:
ATOMS_DATABASE._load()
self.assertDictEqual({"family": "str"}, ATOMS_DATABASE._properties)
self.assertDictEqual({"H": {"family": "non-metal"}}, ATOMS_DATABASE._data)
@@ -102,9 +102,9 @@ def test__load_user_database(self):
}
),
) as m:
- with patch("os.path.exists", spec=True):
+ with patch("pathlib.Path.exists", spec=True):
ATOMS_DATABASE._load("user.json")
- m.assert_called_with("user.json", "r")
+ m.assert_called_with(Path("user.json"), "r")
self.assertDictEqual({"family": "str"}, ATOMS_DATABASE._properties)
self.assertDictEqual(
{"H": {"family": "non-metal"}}, ATOMS_DATABASE._data
@@ -126,7 +126,7 @@ def test___getitem__(self):
ATOMS_DATABASE["H"],
)
with self.assertRaises(AtomsDatabaseError):
- a = ATOMS_DATABASE["INVALID"]
+ _a = ATOMS_DATABASE["INVALID"]
def test___iter__(self):
generator = iter(ATOMS_DATABASE)