From 3a39a4e57ce5fd40994dd7ab7a9e9b12434070de Mon Sep 17 00:00:00 2001 From: Taka Date: Wed, 20 Feb 2019 05:42:49 +0000 Subject: [PATCH] Changed how startup initialization is done. --- vasl_templates/server_settings.py | 3 +- vasl_templates/webapp/__init__.py | 42 ++++++++---- vasl_templates/webapp/files.py | 13 ++-- vasl_templates/webapp/globvars.py | 21 ++++++ vasl_templates/webapp/tests/remote.py | 17 ++--- .../webapp/tests/test_vasl_extensions.py | 3 - vasl_templates/webapp/tests/test_vassal.py | 45 +++++++----- vasl_templates/webapp/vasl_mod.py | 51 ++++---------- vasl_templates/webapp/vassal.py | 7 +- vasl_templates/webapp/vo.py | 41 +++++++---- vasl_templates/webapp/vo_notes.py | 68 +++++++------------ vasl_templates/webapp/webdriver.py | 4 +- 12 files changed, 163 insertions(+), 152 deletions(-) create mode 100644 vasl_templates/webapp/globvars.py diff --git a/vasl_templates/server_settings.py b/vasl_templates/server_settings.py index c9b9231..9ce224a 100644 --- a/vasl_templates/server_settings.py +++ b/vasl_templates/server_settings.py @@ -260,8 +260,7 @@ def install_server_settings( is_startup ): # initialize if is_startup: - # nb: we let the web page show startup messages - msg_store = None + msg_store = None # nb: we let the web page show startup messages else: msg_store = MsgStore() diff --git a/vasl_templates/webapp/__init__.py b/vasl_templates/webapp/__init__.py index 4ef3a8d..b2e29a6 100644 --- a/vasl_templates/webapp/__init__.py +++ b/vasl_templates/webapp/__init__.py @@ -10,7 +10,27 @@ import logging.config from flask import Flask import yaml -from vasl_templates.webapp.config.constants import APP_NAME, APP_VERSION, BASE_DIR +from vasl_templates.webapp.config.constants import BASE_DIR + +# --------------------------------------------------------------------- + +def _on_startup(): + """Do startup initialization.""" + + # configure the VASL module + fname = app.config.get( "VASL_MOD" ) + if fname: + from vasl_templates.webapp.vasl_mod import set_vasl_mod #pylint: disable=cyclic-import + from vasl_templates.webapp.main import startup_msg_store #pylint: disable=cyclic-import + set_vasl_mod( fname, startup_msg_store ) + + # load the vehicle/ordnance listings + from vasl_templates.webapp.vo import load_vo_listings #pylint: disable=cyclic-import + load_vo_listings() + + # load the vehicle/ordnance notes + from vasl_templates.webapp.vo_notes import load_vo_notes #pylint: disable=cyclic-import + load_vo_notes() # --------------------------------------------------------------------- @@ -29,11 +49,10 @@ def load_debug_config( fname ): # --------------------------------------------------------------------- -cleanup_handlers = [] - -def on_sigint( signum, stack ): #pylint: disable=unused-argument +def _on_sigint( signum, stack ): #pylint: disable=unused-argument """Clean up after a SIGINT.""" - for handler in cleanup_handlers: + from vasl_templates.webapp import globvars #pylint: disable=cyclic-import + for handler in globvars.cleanup_handlers: handler() raise SystemExit() @@ -77,14 +96,7 @@ if app.config.get( "ENABLE_REMOTE_TEST_CONTROL" ): import vasl_templates.webapp.testing #pylint: disable=cyclic-import # install our signal handler (must be done in the main thread) -signal.signal( signal.SIGINT, on_sigint ) - -# --------------------------------------------------------------------- +signal.signal( signal.SIGINT, _on_sigint ) -@app.context_processor -def inject_template_params(): - """Inject template parameters into Jinja2.""" - return { - "APP_NAME": APP_NAME, - "APP_VERSION": APP_VERSION, - } +# register startup initialization +app.before_first_request( _on_startup ) diff --git a/vasl_templates/webapp/files.py b/vasl_templates/webapp/files.py index d06ad10..734a9e3 100644 --- a/vasl_templates/webapp/files.py +++ b/vasl_templates/webapp/files.py @@ -8,8 +8,7 @@ import mimetypes from flask import send_file, send_from_directory, jsonify, redirect, url_for, abort -from vasl_templates.webapp import app -from vasl_templates.webapp.vasl_mod import get_vasl_mod +from vasl_templates.webapp import app, globvars from vasl_templates.webapp.utils import resize_image_response, is_empty_file # --------------------------------------------------------------------- @@ -74,12 +73,11 @@ def get_counter_image( gpid, side, index ): """Get a counter image.""" # check if a VASL module has been configured - vasl_mod = get_vasl_mod() - if not vasl_mod: + if not globvars.vasl_mod: return redirect( url_for( "static", filename="images/missing-image.png" ), code=302 ) # return the specified counter image - image_path, image_data = vasl_mod.get_piece_image( gpid, side, int(index) ) + image_path, image_data = globvars.vasl_mod.get_piece_image( gpid, side, int(index) ) if not image_data: abort( 404 ) return send_file( @@ -94,9 +92,8 @@ def get_vasl_piece_info(): """Get information about the VASL pieces.""" # check if a VASL module has been configured - vasl_mod = get_vasl_mod() - if not vasl_mod: + if not globvars.vasl_mod: return jsonify( {} ) # return the VASL piece info - return jsonify( vasl_mod.get_piece_info() ) + return jsonify( globvars.vasl_mod.get_piece_info() ) diff --git a/vasl_templates/webapp/globvars.py b/vasl_templates/webapp/globvars.py new file mode 100644 index 0000000..c99c2d3 --- /dev/null +++ b/vasl_templates/webapp/globvars.py @@ -0,0 +1,21 @@ +""" Global variables. """ + +from vasl_templates.webapp import app +from vasl_templates.webapp.config.constants import APP_NAME, APP_VERSION + +vasl_mod = None +vo_listings = None +vo_notes = None +vo_notes_file_server = None + +cleanup_handlers = [] + +# --------------------------------------------------------------------- + +@app.context_processor +def inject_template_params(): + """Inject template parameters into Jinja2.""" + return { + "APP_NAME": APP_NAME, + "APP_VERSION": APP_VERSION, + } diff --git a/vasl_templates/webapp/tests/remote.py b/vasl_templates/webapp/tests/remote.py index de17f65..5c9da1c 100644 --- a/vasl_templates/webapp/tests/remote.py +++ b/vasl_templates/webapp/tests/remote.py @@ -17,12 +17,11 @@ import random import pytest -from vasl_templates.webapp import app +from vasl_templates.webapp import app, globvars from vasl_templates.webapp.config.constants import DATA_DIR +from vasl_templates.webapp.vasl_mod import set_vasl_mod from vasl_templates.webapp import main as webapp_main from vasl_templates.webapp import snippets as webapp_snippets -from vasl_templates.webapp import vo_notes as webapp_vo_notes -from vasl_templates.webapp.vasl_mod import set_vasl_mod from vasl_templates.webapp import vasl_mod as vasl_mod_module _logger = logging.getLogger( "control_tests" ) @@ -89,6 +88,8 @@ class ControlTests: raise RuntimeError( "Unknown data dir type: {}".format( dtype ) ) _logger.info( "Setting data dir: %s", dname ) self.webapp.config[ "DATA_DIR" ] = dname + from vasl_templates.webapp.vo import load_vo_listings + load_vo_listings() return self def _set_default_scenario( self, fname=None ): @@ -175,13 +176,14 @@ class ControlTests: startup_msg_store.reset() vasl_mod_module.warnings = [] set_vasl_mod( vmod, startup_msg_store ) + from vasl_templates.webapp.vo import load_vo_listings + load_vo_listings() return self def _get_vasl_extns( self ): #pylint: disable=no-self-use """Return the loaded VASL extensions.""" - from vasl_templates.webapp.vasl_mod import get_vasl_mod - extns = get_vasl_mod().get_extns() + extns = globvars.vasl_mod.get_extns() _logger.debug( "Returning VASL extensions:\n%s", "\n".join( "- {}".format( e ) for e in extns ) ) @@ -251,9 +253,8 @@ class ControlTests: dname = None _logger.info( "Setting vehicle/ordnance notes: %s", dname ) app.config["CHAPTER_H_NOTES_DIR"] = dname - with webapp_vo_notes._vo_notes_lock: #pylint: disable=protected-access - webapp_vo_notes._cached_vo_notes = None #pylint: disable=protected-access - webapp_vo_notes._vo_notes_file_server = None #pylint: disable=protected-access + from vasl_templates.webapp.vo_notes import load_vo_notes + load_vo_notes() return self def _set_user_files_dir( self, dtype=None ): diff --git a/vasl_templates/webapp/tests/test_vasl_extensions.py b/vasl_templates/webapp/tests/test_vasl_extensions.py index 4e554bb..5faa5b4 100644 --- a/vasl_templates/webapp/tests/test_vasl_extensions.py +++ b/vasl_templates/webapp/tests/test_vasl_extensions.py @@ -30,7 +30,6 @@ def test_load_vasl_extensions( webapp, webdriver ): # reload the webapp control_tests.set_vasl_mod( vmod="random", extns_dtype="test" ) - webdriver.refresh() _check_warning_msgs( control_tests, expected ) # try loading an extension that has no buildFile @@ -52,7 +51,6 @@ def test_load_vasl_extensions( webapp, webdriver ): # try loading something that's not a ZIP file control_tests.set_test_vasl_extn( fname="test.zip", bin_data=b"This is not a ZIP file." ) \ .set_vasl_mod( vmod="random", extns_dtype="test" ) - webdriver.refresh() _check_warning_msgs( control_tests, "Can't check VASL extension (not a ZIP file):" ) # --------------------------------------------------------------------- @@ -70,7 +68,6 @@ def test_vasl_extension_info( webapp, webdriver ): def do_test( dtype, expected ): #pylint: disable=missing-docstring control_tests.set_vasl_extn_info_dir( dtype=dtype ) \ .set_vasl_mod( vmod="random", extns_dtype="test" ) - webdriver.refresh() _check_warning_msgs( control_tests, expected ) # try loading the VASL extension, with no matching extension info diff --git a/vasl_templates/webapp/tests/test_vassal.py b/vasl_templates/webapp/tests/test_vassal.py index b461716..9ca2cad 100644 --- a/vasl_templates/webapp/tests/test_vassal.py +++ b/vasl_templates/webapp/tests/test_vassal.py @@ -11,6 +11,7 @@ import pytest from vasl_templates.webapp.vassal import VassalShim from vasl_templates.webapp.utils import TempFile, change_extn +from vasl_templates.webapp import globvars from vasl_templates.webapp.tests.utils import \ init_webapp, select_menu_option, get_stored_msg, set_stored_msg, set_stored_msg_marker, wait_for from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario, load_scenario_params, \ @@ -18,6 +19,13 @@ from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario, # --------------------------------------------------------------------- +class DummyVaslMod: + """Dummy VaslMod class that lets us run the VASSAL shim locally (to dump scenarios).""" + def __init__( self, fname ): + self.filename = fname + +# --------------------------------------------------------------------- + @pytest.mark.skipif( not pytest.config.option.vasl_mods, reason="--vasl-mods not specified" ) #pylint: disable=no-member @pytest.mark.skipif( not pytest.config.option.vassal, reason="--vassal not specified" ) #pylint: disable=no-member @pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member @@ -97,8 +105,7 @@ def test_full_update( webapp, webdriver ): # NOTE: We could arguably only do this once, but updating scenarios is the key functionality of the VASSAL shim, # and so it's worth checking that every VASSAL+VASL combination understands its input correctly. fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/full.vsav" ) - vassal_shim = VassalShim() - vsav_dump = vassal_shim.dump_scenario( fname ) + vsav_dump = _dump_vsav( fname ) _check_vsav_dump( vsav_dump, { "scenario": "Somewhere", "players": re.compile( r"American:.*Belgian:" ), @@ -123,7 +130,7 @@ def test_full_update( webapp, webdriver ): # check the results temp_file.write( updated_vsav_data ) temp_file.close() - updated_vsav_dump = vassal_shim.dump_scenario( temp_file.name ) + updated_vsav_dump = _dump_vsav( temp_file.name ) expected = { "scenario": "Modified scenario name (<>{}\"'\\)", "players": re.compile( r"Russian:.*German:" ), @@ -190,8 +197,7 @@ def test_latw_autocreate( webapp, webdriver ): # check the VASL scenario fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/empty.vsav" ) - vassal_shim = VassalShim() - vsav_dump = vassal_shim.dump_scenario( fname ) + vsav_dump = _dump_vsav( fname ) _check_vsav_dump( vsav_dump, {}, ignore_labels ) # update the scenario (German/Russian, no date) @@ -261,8 +267,7 @@ def test_latw_update( webapp, webdriver ): # check the VASL scenario fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/latw.vsav" ) - vassal_shim = VassalShim() - vsav_dump = vassal_shim.dump_scenario( fname ) + vsav_dump = _dump_vsav( fname ) _check_vsav_dump( vsav_dump, { "psk": "Panzerschrek", "atmm": "ATMM check:", # nb: the PF label has no snippet ID "mol-p": "TH#", # nb: the MOL label has no snippet ID @@ -311,8 +316,7 @@ def test_dump_vsav( webapp, webdriver ): # dump the VASL scenario fname = os.path.join( os.path.split(__file__)[0], "fixtures/dump-vsav/labels.vsav" ) - vassal_shim = VassalShim() - vsav_dump = vassal_shim.dump_scenario( fname ) + vsav_dump = _dump_vsav( fname ) # check the result fname = change_extn( fname, ".txt" ) @@ -344,8 +348,7 @@ def test_legacy_labels( webapp, webdriver ): # dump the VASL scenario # NOTE: We implemented snippet ID's in v0.5, this scenario is the "Hill 621" example from v0.4. fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/hill621-legacy.vsav" ) - vassal_shim = VassalShim() - vsav_dump = vassal_shim.dump_scenario( fname ) + vsav_dump = _dump_vsav( fname ) labels = _get_vsav_labels( vsav_dump ) assert len( [ lbl for lbl in labels if "vasl-templates:id" not in lbl ] ) == 20 assert len( [ lbl for lbl in labels if "vasl-templates:id" in lbl ] ) == 0 #pylint: disable=len-as-condition @@ -426,8 +429,7 @@ def test_legacy_latw_labels( webapp, webdriver ): # dump the VASL scenario # NOTE: This scenario contains LATW labels created using v0.4 i.e. they have no snippet ID's. fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/latw-legacy.vsav" ) - vassal_shim = VassalShim() - vsav_dump = vassal_shim.dump_scenario( fname ) + vsav_dump = _dump_vsav( fname ) labels = _get_vsav_labels( vsav_dump ) assert len( [ lbl for lbl in labels if "vasl-templates:id" not in lbl ] ) == 8 assert len( [ lbl for lbl in labels if "vasl-templates:id" in lbl ] ) == 0 #pylint: disable=len-as-condition @@ -505,6 +507,13 @@ def _run_tests( control_tests, func, test_all ): vasl_mods = [ random.choice( vasl_mods ) ] vassal_engines = [ random.choice( vassal_engines ) ] + # FUDGE! If we are running the tests against a remote server, we still need to be able to run + # the VASSAL shim locally (to dump VASSAL save files), so we need to set up things up enough + # for this to work. + if control_tests.server_url: + vasl_mods_local = control_tests._do_get_vasl_mods() #pylint: disable=protected-access + globvars.vasl_mod = DummyVaslMod( random.choice( vasl_mods_local ) ) + # run the test for each VASSAL+VASL for vassal_engine in vassal_engines: control_tests.set_vassal_engine( vengine=vassal_engine ) @@ -559,12 +568,16 @@ def _update_vsav_and_dump( fname, expected ): with TempFile() as temp_file: temp_file.write( updated_vsav_data ) temp_file.close() - vassal_shim = VassalShim() - updated_vsav_dump = vassal_shim.dump_scenario( temp_file.name ) - return updated_vsav_dump + return _dump_vsav( temp_file.name ) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def _dump_vsav( fname ): + """Dump a VASL scenario file.""" + # NOTE: This is run locally, even if we're running the tests against a remote server. + vassal_shim = VassalShim() + return vassal_shim.dump_scenario( fname ) + def _check_vsav_dump( vsav_dump, expected, ignore=None ): """"Check that a VASL scenario dump contains what we expect.""" diff --git a/vasl_templates/webapp/vasl_mod.py b/vasl_templates/webapp/vasl_mod.py index 551d572..6dc0c10 100644 --- a/vasl_templates/webapp/vasl_mod.py +++ b/vasl_templates/webapp/vasl_mod.py @@ -1,7 +1,6 @@ """ Wrapper around a VASL module file and extensions. """ import os -import threading import json import glob import zipfile @@ -11,7 +10,7 @@ import xml.etree.ElementTree import logging _logger = logging.getLogger( "vasl_mod" ) -from vasl_templates.webapp import app +from vasl_templates.webapp import app, globvars from vasl_templates.webapp.config.constants import DATA_DIR SUPPORTED_VASL_MOD_VERSIONS = [ "6.4.0", "6.4.1", "6.4.2", "6.4.3" ] @@ -21,48 +20,24 @@ warnings = [] # nb: for the test suite # --------------------------------------------------------------------- -# NOTE: The lock only controls access to the _vasl_mod variable, not the VaslMod object it points to. -# In practice this doesn't really matter, since it will be loaded once at startup, then never changes; -# it's only the tests that are constantly changing the underlying object. -_vasl_mod_lock = threading.RLock() -_vasl_mod = None - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -def get_vasl_mod(): - """Return the global VaslMod object.""" - with _vasl_mod_lock: - global _vasl_mod - if _vasl_mod is None: - # check if a VASL module has been configured - # NOTE: We will be doing this check every time someone wants the global VaslMod object, - # even if one hasn't been configured, but in all likelihood, everyone will have it configured, - # in which case, the check will only be done once, and the global _vasl_mod variable set. - fname = app.config.get( "VASL_MOD" ) - if fname: - # yup - load it - from vasl_templates.webapp.main import startup_msg_store #pylint: disable=cyclic-import - set_vasl_mod( fname, startup_msg_store ) - return _vasl_mod - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def set_vasl_mod( vmod_fname, msg_store ): """Install a new global VaslMod object.""" - with _vasl_mod_lock: - global _vasl_mod - if vmod_fname: - extns_dir = app.config.get( "VASL_EXTNS_DIR" ) - extns = _load_vasl_extns( extns_dir ) - _vasl_mod = VaslMod( vmod_fname, DATA_DIR, extns ) - if _vasl_mod.vasl_version not in SUPPORTED_VASL_MOD_VERSIONS: + if vmod_fname: + # load and install the specified VASL module + extns_dir = app.config.get( "VASL_EXTNS_DIR" ) + extns = _load_vasl_extns( extns_dir ) + globvars.vasl_mod = VaslMod( vmod_fname, DATA_DIR, extns ) + # make sure the VASL version is one we support + if globvars.vasl_mod.vasl_version not in SUPPORTED_VASL_MOD_VERSIONS: + if msg_store: msg_store.warning( "VASL {} is unsupported.

Things might work, but they might not...".format( - _vasl_mod.vasl_version + globvars.vasl_mod.vasl_version ) ) - else: - _vasl_mod = None + else: + # no VASL module has been specified + globvars.vasl_mod = None def _load_vasl_extns( extn_dir ): #pylint: disable=too-many-locals,too-many-statements,too-many-branches """Locate VASL extensions and their corresponding vehicle/ordnance info files.""" diff --git a/vasl_templates/webapp/vassal.py b/vasl_templates/webapp/vassal.py index b093a82..ae9e1ae 100644 --- a/vasl_templates/webapp/vassal.py +++ b/vasl_templates/webapp/vassal.py @@ -14,9 +14,8 @@ import xml.etree.cElementTree as ET from flask import request -from vasl_templates.webapp import app +from vasl_templates.webapp import app, globvars from vasl_templates.webapp.config.constants import BASE_DIR, IS_FROZEN -from vasl_templates.webapp.vasl_mod import get_vasl_mod from vasl_templates.webapp.utils import TempFile, SimpleError from vasl_templates.webapp.webdriver import WebDriver @@ -257,7 +256,7 @@ class VassalShim: raise SimpleError( "Can't find the VASL boards: {}".format( self.boards_dir ) ) # locate the VASL module - if not get_vasl_mod(): + if not globvars.vasl_mod: raise SimpleError( "The VASL module has not been configured." ) return self._run_vassal_shim( @@ -284,7 +283,7 @@ class VassalShim: args[0] ] if args[0] in ("dump","update"): - args2.append( get_vasl_mod().filename ) + args2.append( globvars.vasl_mod.filename ) args2.extend( args[1:] ) # figure out how long to the let the VASSAL shim run diff --git a/vasl_templates/webapp/vo.py b/vasl_templates/webapp/vo.py index cdce4bb..06bc848 100644 --- a/vasl_templates/webapp/vo.py +++ b/vasl_templates/webapp/vo.py @@ -6,35 +6,53 @@ import logging from flask import request, render_template, jsonify, abort -from vasl_templates.webapp import app +from vasl_templates.webapp import app, globvars from vasl_templates.webapp.config.constants import DATA_DIR -from vasl_templates.webapp.vasl_mod import get_vasl_mod # --------------------------------------------------------------------- @app.route( "/vehicles" ) def get_vehicle_listings(): """Return the vehicle listings.""" - return _do_get_listings( "vehicles" ) + return jsonify( _do_get_listings( "vehicles" ) ) @app.route( "/ordnance" ) def get_ordnance_listings(): """Return the ordnance listings.""" - return _do_get_listings( "ordnance" ) + return jsonify( _do_get_listings( "ordnance" ) ) + +def _do_get_listings( vo_type ): + """Return the vehicle/ordnance listings.""" + if request.args.get("merge_common") == "1" and request.args.get("report") != "1": + # nb: this is the normal case + return globvars.vo_listings[ vo_type ] + else: + # nb: we should only get here during tests + return _do_load_vo_listings( + vo_type, + request.args.get("merge_common") == "1", request.args.get("report") == "1" + ) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -def _do_get_listings( vo_type ): #pylint: disable=too-many-locals,too-many-branches +def load_vo_listings(): + """Load the vehicle/ordnance listings.""" + globvars.vo_listings = { + "vehicles": _do_load_vo_listings( "vehicles", True, False ), + "ordnance": _do_load_vo_listings( "ordnance", True, False ) + } + +def _do_load_vo_listings( vo_type, merge_common, report ): #pylint: disable=too-many-locals,too-many-branches """Load the vehicle/ordnance listings.""" # locate the data directory - if request.args.get( "report" ): + if report: dname = DATA_DIR # nb: always use the real data for reports, not the test fixtures else: dname = app.config.get( "DATA_DIR", DATA_DIR ) dname = os.path.join( dname, vo_type ) if not os.path.isdir( dname ): - abort( 404 ) + raise RuntimeError( "Missing vehicles/ordnance directory: {}".format( dname ) ) # load the listings listings = {} @@ -54,7 +72,7 @@ def _do_get_listings( vo_type ): #pylint: disable=too-many-locals,too-many-branc listings[nat] = json.load( fp ) # merge common entries - if request.args.get( "merge_common" ) == "1": + if merge_common: # merge common Allied/Axis Minor vehicles/ordnance for minor_type in ("allied-minor","axis-minor"): if minor_type+"-common" not in listings: @@ -74,15 +92,14 @@ def _do_get_listings( vo_type ): #pylint: disable=too-many-locals,too-many-branc # apply any changes for VASL extensions # NOTE: We do this here, rather than in VaslMod, because VaslMod is a wrapper around a VASL module, and so # only knows about GPID's and counter images, rather than Chapter H pieces and piece ID's (e.g. "ge/v:001"). - vasl_mod = get_vasl_mod() - if vasl_mod: + if globvars.vasl_mod: # build an index of the pieces piece_index = {} for nat,pieces in listings.items(): for piece in pieces: piece_index[ piece["id"] ] = piece # process each VASL extension - for extn in vasl_mod.get_extns(): + for extn in globvars.vasl_mod.get_extns(): _apply_extn_info( listings, extn[0], extn[1], piece_index, vo_type ) # update nationality variants with the listings from their base nationality @@ -92,7 +109,7 @@ def _do_get_listings( vo_type ): #pylint: disable=too-many-locals,too-many-branc base_nat = nat.split( "~" )[0] listings[nat] = listings[base_nat] + listings[nat] - return jsonify( listings ) + return listings def _apply_extn_info( listings, extn_fname, extn_info, piece_index, vo_type ): """Update the vehicle/ordnance listings for the specified VASL extension.""" diff --git a/vasl_templates/webapp/vo_notes.py b/vasl_templates/webapp/vo_notes.py index 89293bb..63a5436 100644 --- a/vasl_templates/webapp/vo_notes.py +++ b/vasl_templates/webapp/vo_notes.py @@ -2,62 +2,47 @@ # Pokhara, Nepal (DEC/18). import os -import threading import logging from collections import defaultdict from flask import render_template, jsonify, abort -from vasl_templates.webapp import app -from vasl_templates.webapp.vasl_mod import get_vasl_mod +from vasl_templates.webapp import app, globvars from vasl_templates.webapp.files import FileServer from vasl_templates.webapp.utils import resize_image_response, is_image_file, is_empty_file -_vo_notes_lock = threading.RLock() # nb: this controls the cached V/O notes and the FileServer -_cached_vo_notes = None -_vo_notes_file_server = None - # --------------------------------------------------------------------- @app.route( "/vehicles/notes" ) def get_vehicle_notes(): """Return the Chapter H vehicle notes.""" - vo_notes = _do_get_vo_notes( "vehicles" ) - return jsonify( vo_notes ) + return jsonify( globvars.vo_notes[ "vehicles" ] ) @app.route( "/ordnance/notes" ) def get_ordnance_notes(): """Return the Chapter H ordnance notes.""" - vo_notes = _do_get_vo_notes( "ordnance" ) - return jsonify( vo_notes ) + return jsonify( globvars.vo_notes[ "ordnance" ] ) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -def _do_get_vo_notes( vo_type ): #pylint: disable=too-many-statements,too-many-locals,too-many-branches - """Load the Chapter H notes.""" - - # check if we already have the vehicle/ordnance notes - with _vo_notes_lock: - global _cached_vo_notes - if _cached_vo_notes: - return _cached_vo_notes[ vo_type ] +def load_vo_notes(): #pylint: disable=too-many-statements,too-many-locals,too-many-branches + """Load the Chapter H vehicle/ordnance notes.""" # locate the data directory dname = app.config.get( "CHAPTER_H_NOTES_DIR" ) if not dname: - return {} + globvars.vo_notes = { "vehicles": {}, "ordnance": {} } + globvars.file_server = None + return dname = os.path.abspath( dname ) if not os.path.isdir( dname ): - abort( 404 ) - with _vo_notes_lock: - global _vo_notes_file_server - _vo_notes_file_server = FileServer( dname ) + raise RuntimeError( "Missing Chapter H directory: {}".format( dname ) ) + file_server = FileServer( dname ) # generate a list of extension ID's extn_ids = {} - vasl_mod = get_vasl_mod() - if vasl_mod: - extns = vasl_mod.get_extns() + if globvars.vasl_mod: + extns = globvars.vasl_mod.get_extns() extn_ids = set( e[1]["extensionId"] for e in extns ) def get_ma_note_key( nat, fname ): @@ -144,11 +129,9 @@ def _do_get_vo_notes( vo_type ): #pylint: disable=too-many-statements,too-many-l if "chinese" in vo_notes[vo_type2]: vo_notes[vo_type2]["chinese~gmd"] = vo_notes[vo_type2]["chinese"] - with _vo_notes_lock: - # install the new vehicle/ordnance notes - _cached_vo_notes = { k: dict(v) for k,v in vo_notes.items() } - - return _cached_vo_notes[ vo_type ] + # install the vehicle/ordnance notes + globvars.vo_notes = { k: dict(v) for k,v in vo_notes.items() } + globvars.file_server = file_server # --------------------------------------------------------------------- @@ -157,18 +140,15 @@ def get_vo_note( vo_type, nat, key ): """Return a Chapter H vehicle/ordnance note.""" # locate the file - with _vo_notes_lock: - # NOTE: We assume that the client has already loaded the vehicle/ordnance notes. - if not _vo_notes_file_server: - abort( 404 ) - vo_notes = _do_get_vo_notes( vo_type ) - fname = vo_notes.get( nat, {} ).get( key ) - if not fname: - abort( 404 ) - # nb: we ignore placeholder files (return 404 for empty files) - resp = _vo_notes_file_server.serve_file( fname, ignore_empty=True ) - if not resp: - abort( 404 ) + vo_notes = globvars.vo_notes[ vo_type ] + fname = vo_notes.get( nat, {} ).get( key ) + if not fname: + abort( 404 ) + if not globvars.file_server: + abort( 404 ) + resp = globvars.file_server.serve_file( fname, ignore_empty=True ) + if not resp: + abort( 404 ) default_scaling = app.config.get( "CHAPTER_H_IMAGE_SCALING", 100 ) return resize_image_response( resp, default_scaling=default_scaling ) diff --git a/vasl_templates/webapp/webdriver.py b/vasl_templates/webapp/webdriver.py index 0b23e16..a1082ac 100644 --- a/vasl_templates/webapp/webdriver.py +++ b/vasl_templates/webapp/webdriver.py @@ -9,7 +9,7 @@ import logging from PIL import Image, ImageChops from selenium import webdriver -from vasl_templates.webapp import app, cleanup_handlers +from vasl_templates.webapp import app, globvars from vasl_templates.webapp.utils import TempFile, SimpleError _logger = logging.getLogger( "webdriver" ) @@ -200,7 +200,7 @@ class WebDriver: _logger.info( "Cleaning up shared WebDriver: %x", id(wdriver) ) wdriver._do_stop() #pylint: disable=protected-access atexit.register( cleanup ) - cleanup_handlers.append( cleanup ) + globvars.cleanup_handlers.append( cleanup ) return wdriver