Source code for bibmanager.config_manager.config_manager

# Copyright (c) 2018-2024 Patricio Cubillos.
# bibmanager is open-source software under the MIT license (see LICENSE).

__all__ = [
    'help',
    'display',
    'get',
    'set',
    'update_keys',
]

import os
import shutil
import configparser
import textwrap
import pathlib
from packaging import version

from pygments.styles import STYLE_MAP

from .. import bib_manager as bm
from .. import utils as u
from ..__init__ import __version__


styles = textwrap.fill(
    ", ".join(style for style in iter(STYLE_MAP)),
    width=79,
    initial_indent="  ",
    subsequent_indent="  ")


[docs] def help(key): """ Display help information. Parameters ---------- key: String A bibmanager config parameter. """ style_text = ( f"\nThe '{key}' parameter sets the color-syntax style of displayed " "BibTeX entries.\nThe default style is 'autumn'. Available options " f"are:\n{styles}\nSee http://pygments.org/demo/6780986/ for a demo " f"of the style options.\n\nThe current style is '{get(key)}'.") editor_text = ( f"\nThe '{key}' parameter sets the text editor to use when " "editing the\nbibmanager manually (i.e., a call to: bibm edit). By " "default, bibmanager\nuses the OS-default text editor.\n\n" "Typical text editors are: emacs, vim, gedit.\n" "To set the OS-default editor, set text_editor to 'default'.\n" "Note that aliases defined in the .bash are not accessible.\n\n" f"The current text editor is '{get(key)}'.") paper_text = ( f"\nThe '{key}' parameter sets the default paper format for latex " "compilation outputs\n(not for pdflatex, which is automatic). " "Typical options are 'letter'\n(e.g., for ApJ articles) or 'A4' " f"(e.g., for A&A).\n\nThe current paper format is: '{get(key)}'.") token_text = ( f"\nThe '{key}' parameter sets the ADS token required for ADS requests." "\nTo obtain a token, follow the steps described here:" "\n https://github.com/adsabs/adsabs-dev-api#access" f"\n\nThe current ADS token is '{get(key)}'") display_text = ( f"\nThe '{key}' parameter sets the number of entries to show at " "a time,\nfor an ADS search query.\n\n" f"The current number of entries to display is {get(key)}.") home_text = ( f"\nThe '{key}' parameter sets the home directory for the Bibmanager " f"database.\n\nThe current directory is '{get(key)}'.") if key == 'style': print(style_text) elif key == 'text_editor': print(editor_text) elif key == 'paper': print(paper_text) elif key == 'ads_token': print(token_text) elif key == 'ads_display': print(display_text) elif key == 'home': print(home_text) else: # Call get() to trigger exception: get(key)
[docs] def display(key=None): """ Display the value(s) of the bibmanager config file on the prompt. Parameters ---------- key: String bibmanager config parameter to display. Leave as None to display the values from all parameters. Examples -------- >>> import bibmanager.config_manager as cm >>> # Show all parameters and values: >>> cm.display() bibmanager configuration file: PARAMETER VALUE ----------- ----- style autumn text_editor default paper letter ads_token None ads_display 20 home /home/user/.bibmanager/ >>> # Show an specific parameter: >>> cm.display('text_editor') text_editor: default """ if key is not None: print(f"{key}: {get(key)}") else: config = configparser.ConfigParser() config.read(u.HOME + 'config') print("\nbibmanager configuration file:" "\nPARAMETER VALUE" "\n----------- -----") for key, value in config['BIBMANAGER'].items(): print(f"{key:11} {value}")
[docs] def get(key): """ Get the value of a parameter in the bibmanager config file. Parameters ---------- key: String The requested parameter name. Returns ------- value: String Value of the requested parameter. Examples -------- >>> import bibmanager.config_manager as cm >>> cm.get('paper') 'letter' >>> cm.get('style') 'autumn' """ config = configparser.ConfigParser() config.read(u.HOME + 'config') if not config.has_option('BIBMANAGER', key): rconfig = configparser.ConfigParser() rconfig.read(u.ROOT+'config') raise ValueError( f"'{key}' is not a valid bibmanager config parameter.\n" f"The available parameters are:\n {rconfig.options('BIBMANAGER')}") return config.get('BIBMANAGER', key)
[docs] def set(key, value): """ Set the value of a bibmanager config parameter. Parameters ---------- key: String bibmanager config parameter to set. value: String Value to set for input parameter. Examples -------- >>> import bibmanager.config_manager as cm >>> # Update text editor: >>> cm.set('text_editor', 'vim') text_editor updated to: vim. >>> # Invalid bibmanager parameter: >>> cm.set('styles', 'arduino') ValueError: 'styles' is not a valid bibmanager config parameter. The available parameters are: ['style', 'text_editor', 'paper', 'ads_token', 'ads_display', 'home'] >>> # Attempt to set an invalid style: >>> cm.set('style', 'fake_style') ValueError: 'fake_style' is not a valid style option. Available options are: default, emacs, friendly, colorful, autumn, murphy, manni, monokai, perldoc, pastie, borland, trac, native, fruity, bw, vim, vs, tango, rrt, xcode, igor, paraiso-light, paraiso-dark, lovelace, algol, algol_nu, arduino, rainbow_dash, abap >>> # Attempt to set an invalid command for text_editor: >>> cm.set('text_editor', 'my_own_editor') ValueError: 'my_own_editor' is not a valid text editor. >>> # Beware, one can still set a valid command that doesn't edit text: >>> cm.set('text_editor', 'less') text_editor updated to: less. """ config = configparser.ConfigParser() config.read(u.HOME + 'config') # Use get on invalid key to raise an error: if not config.has_option('BIBMANAGER', key): get(key) # Check for exceptions: if key == 'style' and value not in STYLE_MAP.keys(): raise ValueError( f"'{value}' is not a valid style option. " f"Available options are:\n{styles}") # The code identifies invalid commands, but cannot assure that a # command actually applies to a text file. if key == 'text_editor' \ and value != 'default' \ and shutil.which(value) is None: raise ValueError(f"'{value}' is not a valid text editor.") if key == 'ads_display' and (not value.isnumeric() or value=='0'): raise ValueError(f"The {key} value must be a positive integer.") if key == 'home': value = os.path.abspath(os.path.expanduser(value)) + '/' new_home = pathlib.Path(value) if not new_home.parent.is_dir(): raise ValueError( f"The {key} value must have an existing parent folder") if new_home.suffix != '': raise ValueError(f"The {key} value cannot have a file extension") # Make sure folders will exist: new_home.mkdir(exist_ok=True) pathlib.Path(f'{value}pdf').mkdir(exist_ok=True) # Files to move (config has to stay at u.HOME): bm_files = [ u.BM_DATABASE(), u.BM_BIBFILE(), u.BM_CACHE(), u.BM_HISTORY_SEARCH(), u.BM_HISTORY_ADS(), u.BM_HISTORY_PDF(), ] pdf_files = [ f'{u.BM_PDF()}{bib.pdf}' for bib in bm.load() if bib.pdf is not None if os.path.isfile(f'{u.BM_PDF()}{bib.pdf}') ] # Merge if there is already a Bibmanager database in new_home: new_database = f'{new_home}/{os.path.basename(u.BM_DATABASE())}' if os.path.isfile(new_database): pickle_ver = bm.get_version(new_database) if version.parse(__version__) < version.parse(pickle_ver): print(f"Bibmanager version ({__version__}) is older than saved " f"database. Please update to a version >= {pickle_ver}.") return new_biblio = f'{new_home}/{os.path.basename(u.BM_BIBFILE())}' bm.merge(new=bm.read_file(new_biblio), take='new') # Move (overwrite) database files: bm_files = [bm_file for bm_file in bm_files if os.path.isfile(bm_file)] new_files = os.listdir(new_home) for bm_file in bm_files: if os.path.basename(bm_file) in new_files: os.remove(f'{new_home}/{os.path.basename(bm_file)}') shutil.move(bm_file, str(new_home)) # Move (overwrite) PDF files: new_pdfs = os.listdir(f'{new_home}/pdf') for pdf_file in pdf_files: if os.path.basename(pdf_file) in new_pdfs: os.remove(f'{new_home}/pdf/{os.path.basename(pdf_file)}') shutil.move(pdf_file, f'{new_home}/pdf/') # Set value if there were no exceptions raised: config.set('BIBMANAGER', key, value) with open(u.HOME+'config', 'w', encoding='utf-8') as configfile: config.write(configfile) print(f'{key} updated to: {value}.')
[docs] def update_keys(): """Update config in HOME with keys from ROOT, without overwriting values.""" config_root = configparser.ConfigParser() config_root.read(u.ROOT+'config') config_root.set('BIBMANAGER', 'home', u.HOME) # Won't complain if HOME+'config' does not exist (keep ROOT values): config_root.read(u.HOME+'config') with open(u.HOME+'config', 'w', encoding='utf-8') as configfile: config_root.write(configfile)