Source code for core.lnp

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""PyLNP main library."""

import os
import sys

from . import log
from .json_config import JSONConfiguration

VERSION = 'dev'


[docs] class UI(object): """ Specifies the interface required by the core PyLNP library for communicating with the user. Provided for reference; UIs do not need to inherit from this. """
[docs] def start(self): """Notifies the UI to start. On return, PyLNP will terminate."""
[docs] def on_update_available(self): """ Called when an update is available. Use this to show a notification and prompt the user for further action. """
[docs] def on_program_running(self, path, is_df): """ Called when attempting to launch a program that is already running. <path> provides the path to the program that is being launched, so you can request a forced launch. <is_df> specifies if the program is DF (True) or a utility (False). """
[docs] def on_invalid_config(self, errors): """ Called before running DF if an invalid configuration is detected. <errors> contains a list of discovered problems, which should be shown to the user. A true return value will launch DF anyway; a false return value cancels. """
[docs] def on_request_update_permission(self, interval): """ Called when PyLNP.json specifies a desired update interval but the user configuration does not hold a value for this. <interval> contains the number of days between update checks. A true return value will change the configuration to use the specified interval. A false return value will turn off automatic update checks. """
[docs] def on_query_migration(self): """ When no saves are detected, this function will be called. This should provide the user with an option to import a previous DF install or starter pack into the newly selected DF version. """
lnp = None
[docs] class PyLNP(object): """ PyLNP library class. Acts as an abstraction layer between the UI and the Dwarf Fortress instance. """ # pylint: disable=too-many-instance-attributes def __init__(self): """Constructor for the PyLNP library.""" # pylint:disable=global-statement global lnp lnp = self self.args = self.parse_commandline() self.BASEDIR = '.' if sys.platform == 'win32': self.os = 'win' elif sys.platform.startswith('linux'): self.os = 'linux' elif sys.platform == 'darwin': self.os = 'osx' self.bundle = '' if hasattr(sys, 'frozen'): self.bundle = self.os os.chdir(os.path.dirname(sys.executable)) if self.bundle == 'osx': # OS X bundles start in different directory os.chdir('../../..') else: os.chdir(os.path.join(os.path.dirname(__file__), '..')) from . import update self.folders = [] self.df_info = None self.settings = None self.running = {} self.autorun = [] self.updater = None self.config = None self.userconfig = None self.ui = None self.initialize_program() self.initialize_df() self.new_version = None self.initialize_ui() update.check_update() from . import paths save_dir = paths.get('save') saves_exist = os.path.isdir(save_dir) and os.listdir(save_dir) if paths.get('df') and not saves_exist: self.ui.on_query_migration() self.ui.start()
[docs] def initialize_program(self): """Initializes the main program (errorlog, path registration, etc.).""" from . import errorlog, paths, utilities self.BASEDIR = '.' self.detect_basedir() paths.clear() paths.register('root', self.BASEDIR) errorlog.start() paths.register('lnp', self.BASEDIR, 'LNP') if not os.path.isdir(paths.get('lnp')): log.w('LNP folder is missing!') paths.register('keybinds', paths.get('lnp'), 'Keybinds') paths.register('graphics', paths.get('lnp'), 'Graphics') paths.register('utilities', paths.get('lnp'), 'Utilities') paths.register('colors', paths.get('lnp'), 'Colors') paths.register('embarks', paths.get('lnp'), 'Embarks') paths.register('tilesets', paths.get('lnp'), 'Tilesets') paths.register('baselines', paths.get('lnp'), 'Baselines') paths.register('mods', paths.get('lnp'), 'Mods') config_file = 'PyLNP.json' if os.access(paths.get('lnp', 'PyLNP.json'), os.F_OK): config_file = paths.get('lnp', 'PyLNP.json') default_config = { "folders": [ ["Savegame folder", "<df>/data/save"], ["Utilities folder", "LNP/Utilities"], ["Graphics folder", "LNP/Graphics"], ["-", "-"], ["Main folder", ""], ["LNP folder", "LNP"], ["Dwarf Fortress folder", "<df>"], ["Init folder", "<df>/data/init"] ], "links": [ ["DF Homepage", "https://www.bay12games.com/dwarves/"], ["DF Wiki", "https://dwarffortresswiki.org/"], ["DF Forums", "http://www.bay12forums.com/smf/"] ], "to_import": [ ['text_prepend', '<df>/gamelog.txt'], ['text_prepend', '<df>/ss_fix.log'], ['text_prepend', '<dfhack_config>/dfhack.history'], ['copy_add', '<df>/data/save'], ['copy_add', '<df>/soundsense', 'LNP/Utilities/Soundsense/packs'], ['copy_add', 'LNP/Utilities/Soundsense/packs'], ['copy_add', 'User Generated Content'] ], "hideUtilityPath": False, "hideUtilityExt": False, "updates": { "updateMethod": "" } } self.config = JSONConfiguration(config_file, default_config) self.userconfig = JSONConfiguration('PyLNP.user') self.autorun = [] utilities.load_autorun() if self.args.terminal_test_parent: from . import terminal errorlog.stop() sys.exit(terminal.terminal_test_parent( self.args.terminal_test_parent[0])) if self.args.terminal_test_child: from . import terminal errorlog.stop() sys.exit(terminal.terminal_test_child( self.args.terminal_test_child[0]))
[docs] def initialize_df(self): """Initializes the DF folder and related variables.""" from . import df self.df_info = None self.folders = [] self.settings = None df.find_df_folder()
[docs] def initialize_ui(self): """Instantiates the UI object.""" from tkgui.tkgui import TkGui self.ui = TkGui()
[docs] def reload_program(self): """Reloads the program to allow the user to change DF folders.""" self.args.df_folder = None self.initialize_program() self.initialize_df() self.initialize_ui() self.ui.start()
[docs] def parse_commandline(self): """Parses and acts on command line options.""" args = self.get_commandline_args() if args.debug == 1: log.set_level(log.DEBUG) elif args.debug is not None and args.debug > 1: log.set_level(log.VERBOSE) if args.release_prep: args.raw_lint = True log.d(args) return args
[docs] @staticmethod def get_commandline_args(): """Responsible for the actual parsing of command line options.""" import argparse parser = argparse.ArgumentParser( description="PyLNP " + VERSION) parser.add_argument( '-d', '--debug', action='count', help='Turn on debugging output (use twice for extra verbosity)') parser.add_argument( '--raw-lint', action='store_true', help='Verify contents of raw files and exit') parser.add_argument( 'df_folder', nargs='?', help='Dwarf Fortress folder to use (if it exists)') parser.add_argument( '--version', action='version', version="PyLNP " + VERSION) parser.add_argument( '--df-executable', action='store', help='Override DF/DFHack executable name') parser.add_argument( '--release-prep', action='store_true', help=argparse.SUPPRESS) parser.add_argument( '--terminal-test-parent', nargs=1, help=argparse.SUPPRESS) parser.add_argument( '--terminal-test-child', nargs=1, help=argparse.SUPPRESS) return parser.parse_known_args()[0]
[docs] def save_config(self): """Saves LNP configuration.""" self.userconfig.save_data()
[docs] def macos_check_translocated(self): """Verify that macOS isn't isolating our application.""" assert self.os == 'osx' if '/AppTranslocation/' in sys.executable: try: from tkinter import messagebox except ImportError: import tkMessageBox as messagebox messagebox.showinfo( 'Cannot launch PyLNP', 'PyLNP cannot be run from a disk image or from the Downloads ' 'folder. Please copy the PyLNP app and its other Dwarf ' 'Fortress files elsewhere, such as to the Applications folder.')
[docs] def detect_basedir(self): """Detects the location of Dwarf Fortress by walking up the directory tree.""" prev_path = '.' from . import df try: while os.path.abspath(self.BASEDIR) != prev_path: df.find_df_folders() if len(self.folders) != 0: return prev_path = os.path.abspath(self.BASEDIR) self.BASEDIR = os.path.join(self.BASEDIR, '..') except UnicodeDecodeError: # This seems to no longer be an issue, but leaving in the check # just in case log.e( "PyLNP is being stored in a path containing non-ASCII " "characters, and cannot continue. Folder names may only use " "the characters A-Z, 0-9, and basic punctuation.\n" "Alternatively, you may run PyLNP from source using Python 3.") sys.exit(1) log.e("Could not find any Dwarf Fortress installations.") if self.os == 'osx': self.macos_check_translocated() sys.exit(2)
# vim:expandtab