Source code for psi4.driver.pluginutil

# Psi4: an open-source quantum chemistry software package
# Copyright (c) 2007-2023 The Psi4 Developers.
# The copyrights for code used from other parties are included in
# the corresponding files.
# This file is part of Psi4.
# Psi4 is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3.
# Psi4 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public License along
# with Psi4; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

__all__ = [

import os
import sys
from pathlib import Path

from psi4 import core
from psi4.driver.util import tty

[docs]def sanitize_name(name: str) -> str: """Function to return *name* in coded form, stripped of characters that confuse filenames, characters into lowercase, ``+`` into ``p``, ``*`` into ``s``, and ``(``, ``)``, ``-``, & ``,`` into ``_``. Also checks the sanitized name against a list of restricted C++ keywords. """ if name[0].isalpha(): temp = name.lower() temp = temp.replace('+', 'p') temp = temp.replace('*', 's') temp = temp.replace('(', '_') temp = temp.replace(')', '_') temp = temp.replace(',', '_') temp = temp.replace('-', '_') # Taken from cpp_keywords = [ "alignas", "alignof", "and", "and_eq", "asm", "atomic_cancel", "atomic_commit", "atomic_noexcept", "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", "char16_t", "char32_t", "class", "compl", "concept", "const", "constexpr", "const_cast", "continue", "decltype", "default", "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", "float", "for", "friend", "goto", "if", "import", "inline", "int", "long", "module", "mutable", "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq", "private", "protected", "public", "register", "reinterpret_cast", "requires", "return", "short", "signed", "sizeof", "static", "static_assert", "static_cast", "struct", "switch", "synchronized", "template", "this", "thread_local", "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq", # Identifiers with special meanings" "override", "final", "transaction_safe", "transaction_safe_dynamic", # Preprocessor tokens "if", "elif", "else", "endif", "defined", "ifdef", "ifndef", "define", "undef", "include", "line", "error", "pragma", "_pragma", # C++20 "char8_t", "consteval", "constinit", "co_await", "co_return", "co_yield", "reflexpr", ] if temp in cpp_keywords: tty.die("The plugin name you provided is a C++ reserved keyword. Please provide a different name.") return temp else: tty.die("Plugin name must begin with a letter.")
# Determine the available plugins available_plugins = [] psidatadir = Path(core.get_datadir()) plugin_path = psidatadir / "plugin" for sdir in os.listdir(plugin_path): if (plugin_path / sdir).is_dir(): available_plugins.append(sdir)
[docs]def create_plugin(name: str, template: str) -> None: f"""Generate plugin in directory with sanitized *name* based upon *template*. Parameters ---------- name Name of plugin. Should not have any fancy characters or reserved keywords. template {{{available_plugins}}} Which existing template to model off of. """ name = sanitize_name(name) template_path = plugin_path / template # Create, but do not overwrite, plugin directory if os.path.exists(name): tty.error("""Plugin directory "{}" already exists.""".format(name)) # Do a first pass to determine the template temp_files template_files = os.listdir(template_path) source_files = [] for temp_file in template_files: target_file = temp_file if temp_file.endswith('.template'): target_file = temp_file[0:-9] if temp_file.endswith('.cc.template'): source_files.append(target_file) tty.hline("""Creating "{}" with "{}" template.""".format(name, template)) os.mkdir(name) created_files = [] for source_file in template_files: # Skip swp files if source_file.endswith(".swp"): continue target_file = source_file if source_file.endswith('.template'): target_file = source_file[0:-9] try: print(template_path / source_file) contents = (template_path / source_file).read_text() except IOError as err: tty.error("""Unable to open {} template.""".format(source_file)) tty.error(err) sys.exit(1) contents = contents.replace('@plugin@', name) contents = contents.replace('@Plugin@', name.capitalize()) contents = contents.replace('@PLUGIN@', name.upper()) contents = contents.replace('@sources@', ' '.join(source_files)) try: (Path(name) / target_file).write_text(contents) created_files.append(target_file) except IOError as err: tty.error("""Unable to create {}""".format(target_file)) tty.error(err) sys.exit(1)"Created plugin files (in {} as {}): ".format(name, template), ", ".join(created_files)) sys.exit(0)