Source code for pyiron_base.database.manager

# coding: utf-8
# Copyright (c) Max-Planck-Institut für Eisenforschung GmbH - Computational Materials Design (CM) Department
# Distributed under the terms of "New BSD License", see the LICENSE file.
"""
A class for mediating connections to SQL databases.
"""

import os
from typing import Optional, Union
from urllib.parse import quote_plus

from pyiron_snippets.logger import logger
from pyiron_snippets.singleton import Singleton

from pyiron_base.state.settings import settings as s

__author__ = "Jan Janssen, Liam Huber"
__copyright__ = (
    "Copyright 2020, Max-Planck-Institut für Eisenforschung GmbH"
    " - Computational Materials Design (CM) Department"
)
__version__ = "0.0"
__maintainer__ = "Liam Huber"
__email__ = "huber@mpie.de"
__status__ = "development"
__date__ = "Sep 24, 2021"


[docs] class DatabaseManager(metaclass=Singleton):
[docs] def __init__(self): self._database = None self._use_local_database = False self._database_is_disabled = s.configuration["disable_database"] self.open_connection()
@property def database(self): return self._database @property def using_local_database(self) -> bool: return self._use_local_database @property def database_is_disabled(self) -> bool: return self._database_is_disabled @property def project_check_enabled(self) -> bool: if self.database_is_disabled: return False else: return s.configuration["project_check_enabled"] @property def connection_timeout(self) -> int: """ Get the connection timeout in seconds. Zero means close the database after every connection. Returns: int: timeout in seconds """ return s.configuration["connection_timeout"] @connection_timeout.setter def connection_timeout(self, val: int) -> None: s.configuration["connection_timeout"] = val @staticmethod def _sqlalchemy_string(prefix: str, user: str, key: str, host: str, database: str): key = quote_plus(key) return f"{prefix}://{user}:{key}@{host}/{database}" def _credentialed_sqalchemy_string(self, prefix: str): return self._sqlalchemy_string( prefix, s.configuration["user"], s.configuration["sql_user_key"], s.configuration["sql_host"], s.configuration["sql_database"], ) @property def sql_connection_string(self) -> str: sql_type = s.configuration["sql_type"] if sql_type == "Postgres": return self._credentialed_sqalchemy_string("postgresql") elif sql_type == "MySQL": return self._credentialed_sqalchemy_string("mysql+pymysql") elif sql_type == "SQLalchemy": return s.configuration["sql_connection_string"] elif sql_type == "SQLite": return "sqlite:///" + s.configuration["sql_file"].replace("\\", "/") else: raise ValueError( f"Invalid SQL type {sql_type} -- This should have been caught at input processing, please contact the " f"developers" ) @property def sql_view_connection_string(self) -> Union[str, None]: if s.configuration["sql_view_user"] is None: return None else: return self._sqlalchemy_string( "postgresql", s.configuration["sql_view_user"], s.configuration["sql_view_user_key"], s.configuration["sql_host"], s.configuration["sql_database"], ) @property def sql_table_name(self) -> str: return s.configuration["sql_table_name"]
[docs] def open_connection(self) -> None: """ Internal function to open the connection to the database. Only after this function is called the database is accessable. """ if self._database is None and not self.database_is_disabled: from pyiron_base.database.generic import DatabaseAccess self._database = DatabaseAccess( self.sql_connection_string, self.sql_table_name, timeout=self.connection_timeout, )
[docs] def switch_to_local_database( self, file_name: str = "pyiron.db", cwd: Optional[str] = None ) -> None: """ Swtich to an local SQLite based database. Args: file_name (str): SQLite database file name cwd (str/None): directory where the SQLite database file is located in """ if self.using_local_database: logger.info("Database is already in local mode or disabled!") else: if cwd is None and not os.path.isabs(file_name): file_name = os.path.join(os.path.abspath(os.path.curdir), file_name) elif cwd is not None: file_name = os.path.join(cwd, file_name) self.close_connection() self.open_local_sqlite_connection( connection_string="sqlite:///" + file_name )
def open_local_sqlite_connection(self, connection_string: str) -> None: from pyiron_base.database.generic import DatabaseAccess self._database = DatabaseAccess(connection_string, self.sql_table_name) self._use_local_database = True self._database_is_disabled = False
[docs] def switch_to_central_database(self) -> None: """ Switch to central database """ if self.using_local_database: self.update() else: logger.info("Database is already in central mode or disabled!")
[docs] def switch_to_viewer_mode(self) -> None: """ Switch from user mode to viewer mode - if view_mode is enable pyiron has read only access to the database. """ logger.info("Viewer Mode is not available any more!") raise DeprecationWarning("Viewer Mode is not available any more!")
[docs] def switch_to_user_mode(self) -> None: """ Switch from viewer mode to user mode - if view_mode is enable pyiron has read only access to the database. """ logger.info("Database is already in user mode!") raise DeprecationWarning( "Viewer Mode is not available any more: already in user_mode!" )
[docs] def close_connection(self) -> None: """ Internal function to close the connection to the database. """ if self._database is not None: self._database.conn.close() self._database = None
[docs] def top_path(self, full_path: str) -> Union[str, None]: """ Validated that the full_path is a sub directory of one of the pyrion environments loaded. Args: full_path (str): path Returns: str: path """ full_path = full_path if full_path.endswith("/") else full_path + "/" for path in s.configuration["project_paths"]: if path in full_path: return path if self.project_check_enabled: raise ValueError( f"the current path {full_path} is not included in the .pyiron configuration 'project_paths': " f"{s.configuration['project_paths']}" ) else: return None
[docs] def update(self) -> None: """ Warning: Database interaction does not have written spec. This method does a thing. It might not be the thing you want. """ self.close_connection() self._use_local_database = False self._database_is_disabled = s.configuration["disable_database"] self.open_connection()
database = DatabaseManager()