mirror of
https://github.com/inventree/InvenTree.git
synced 2026-09-27 14:16:02 +00:00
evaluate enviroment once (#12890)
This commit is contained in:
@@ -35,65 +35,155 @@ def isAppLoaded(app_name: str) -> bool:
|
|||||||
return app_name in _loaded_apps
|
return app_name in _loaded_apps
|
||||||
|
|
||||||
|
|
||||||
|
# Cached introspection of the command line arguments
|
||||||
|
ARGV_COMMANDS: dict[str, tuple[str, ...]] = {
|
||||||
|
# Running in test mode
|
||||||
|
'test': ('test', 'pytest'),
|
||||||
|
# The 'test' management command - not pytest
|
||||||
|
'test_command': ('test',),
|
||||||
|
# Collecting the available plugins
|
||||||
|
'collect_plugins': ('collectplugins',),
|
||||||
|
# Listing the installed apps
|
||||||
|
'list_apps': ('list_apps',),
|
||||||
|
# Running an interactive shell session
|
||||||
|
'shell': ('shell',),
|
||||||
|
# Running as a background worker
|
||||||
|
'worker': ('qcluster',),
|
||||||
|
# Running the django development server
|
||||||
|
'runserver': ('runserver',),
|
||||||
|
# Waiting for the database to become available
|
||||||
|
'wait_for_db': ('wait_for_db',),
|
||||||
|
# #### #
|
||||||
|
# More complex groups
|
||||||
|
# #### #
|
||||||
|
# Importing (or exporting) database records
|
||||||
|
'import_data': ('flush', 'loaddata', 'bulkloaddata', 'dumpdata', 'bulkdumpdata'),
|
||||||
|
# Running database migrations
|
||||||
|
'migrations': ('migrate', 'makemigrations', 'showmigrations', 'runmigrations'),
|
||||||
|
# Rebuilding database records
|
||||||
|
'rebuild_data': (
|
||||||
|
'rebuild',
|
||||||
|
'rebuild_models',
|
||||||
|
'rebuild_thumbnails',
|
||||||
|
'remove_stale_contenttypes',
|
||||||
|
),
|
||||||
|
# Running a backup / restore operation
|
||||||
|
'backup': (
|
||||||
|
'backup',
|
||||||
|
'restore',
|
||||||
|
'dbbackup',
|
||||||
|
'dbrestore',
|
||||||
|
'mediabackup',
|
||||||
|
'mediarestore',
|
||||||
|
),
|
||||||
|
# Read-only commands, which should not trigger any database writes
|
||||||
|
'read_only': (
|
||||||
|
'help',
|
||||||
|
'check',
|
||||||
|
'shell',
|
||||||
|
'sqlflush',
|
||||||
|
'list_apps',
|
||||||
|
'wait_for_db',
|
||||||
|
'spectactular',
|
||||||
|
'makemessages',
|
||||||
|
'collectstatic',
|
||||||
|
'showmigrations',
|
||||||
|
'compilemessages',
|
||||||
|
),
|
||||||
|
# Commands which should *not* trigger schema generation
|
||||||
|
'schema_excluded': (
|
||||||
|
'compilemessages',
|
||||||
|
'createsuperuser',
|
||||||
|
'clean_settings',
|
||||||
|
'collectstatic',
|
||||||
|
'makemessages',
|
||||||
|
'wait_for_db',
|
||||||
|
'list_apps',
|
||||||
|
'gunicorn',
|
||||||
|
'sqlflush',
|
||||||
|
'qcluster',
|
||||||
|
'check',
|
||||||
|
'shell',
|
||||||
|
'help',
|
||||||
|
),
|
||||||
|
# Commands which *do* trigger schema generation
|
||||||
|
'schema_generation': (
|
||||||
|
'schema',
|
||||||
|
'spectactular',
|
||||||
|
# schema adjacent calls
|
||||||
|
'export_settings_definitions',
|
||||||
|
'export_tags',
|
||||||
|
'export_filters',
|
||||||
|
'export_report_context',
|
||||||
|
),
|
||||||
|
# Commands which must not touch the database during the app 'ready' phase
|
||||||
|
'database_excluded': (
|
||||||
|
'compilemessages',
|
||||||
|
'createsuperuser',
|
||||||
|
'collectstatic',
|
||||||
|
'makemessages',
|
||||||
|
'spectactular',
|
||||||
|
'wait_for_db',
|
||||||
|
'check',
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _introspectCommands(argv: list[str]):
|
||||||
|
"""Introspect the provided command line arguments."""
|
||||||
|
args = set(argv)
|
||||||
|
entrypoint = argv[0] if argv else ''
|
||||||
|
|
||||||
|
_context = {
|
||||||
|
key: not args.isdisjoint(commands) for key, commands in ARGV_COMMANDS.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
# The entrypoint itself can indicate the context
|
||||||
|
_context['pytest_entrypoint'] = entrypoint.endswith('pytest')
|
||||||
|
_context['gunicorn_entrypoint'] = 'gunicorn' in entrypoint
|
||||||
|
|
||||||
|
# The development server is running without the auto-reloader
|
||||||
|
_context['noreload'] = '--noreload' in args
|
||||||
|
|
||||||
|
return _context
|
||||||
|
|
||||||
|
|
||||||
|
cmd_context = _introspectCommands(sys.argv)
|
||||||
|
|
||||||
|
|
||||||
def isInTestMode():
|
def isInTestMode():
|
||||||
"""Returns True if the database is in testing mode."""
|
"""Returns True if the database is in testing mode."""
|
||||||
return any(x in sys.argv for x in ['test', 'pytest']) or sys.argv[0].endswith(
|
return cmd_context['test'] or cmd_context['pytest_entrypoint']
|
||||||
'pytest'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def isWaitingForDatabase():
|
def isWaitingForDatabase():
|
||||||
"""Return True if we are currently waiting for the database to be ready."""
|
"""Return True if we are currently waiting for the database to be ready."""
|
||||||
return 'wait_for_db' in sys.argv
|
return cmd_context['wait_for_db']
|
||||||
|
|
||||||
|
|
||||||
def isImportingData():
|
def isImportingData():
|
||||||
"""Returns True if the database is currently importing (or exporting) data, e.g. 'loaddata' command is performed."""
|
"""Returns True if the database is currently importing (or exporting) data, e.g. 'loaddata' command is performed."""
|
||||||
return any(
|
return cmd_context['import_data']
|
||||||
x in sys.argv
|
|
||||||
for x in ['flush', 'loaddata', 'bulkloaddata', 'dumpdata', 'bulkdumpdata']
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def isRunningMigrations():
|
def isRunningMigrations():
|
||||||
"""Return True if the database is currently running migrations."""
|
"""Return True if the database is currently running migrations."""
|
||||||
return any(
|
return cmd_context['migrations']
|
||||||
x in sys.argv
|
|
||||||
for x in ['migrate', 'makemigrations', 'showmigrations', 'runmigrations']
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def isRebuildingData():
|
def isRebuildingData():
|
||||||
"""Return true if any of the rebuilding commands are being executed."""
|
"""Return true if any of the rebuilding commands are being executed."""
|
||||||
return any(
|
return cmd_context['rebuild_data']
|
||||||
x in sys.argv
|
|
||||||
for x in [
|
|
||||||
'rebuild',
|
|
||||||
'rebuild_models',
|
|
||||||
'rebuild_thumbnails',
|
|
||||||
'remove_stale_contenttypes',
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def isRunningBackup():
|
def isRunningBackup():
|
||||||
"""Return true if any of the backup commands are being executed."""
|
"""Return true if any of the backup commands are being executed."""
|
||||||
return any(
|
return cmd_context['backup']
|
||||||
x in sys.argv
|
|
||||||
for x in [
|
|
||||||
'backup',
|
|
||||||
'restore',
|
|
||||||
'dbbackup',
|
|
||||||
'dbrestore',
|
|
||||||
'mediabackup',
|
|
||||||
'mediarestore',
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def isCollectingPlugins():
|
def isCollectingPlugins():
|
||||||
"""Return True if the 'collectplugins' command is being executed."""
|
"""Return True if the 'collectplugins' command is being executed."""
|
||||||
return 'collectplugins' in sys.argv
|
return cmd_context['collect_plugins']
|
||||||
|
|
||||||
|
|
||||||
# This variable is used to cache the result of the isGeneratingSchema function, to prevent multiple executions of the same checks
|
# This variable is used to cache the result of the isGeneratingSchema function, to prevent multiple executions of the same checks
|
||||||
@@ -133,36 +223,10 @@ def isGeneratingSchema():
|
|||||||
return _setGeneratingSchema(False)
|
return _setGeneratingSchema(False)
|
||||||
|
|
||||||
# Additional set of commands which should not trigger schema generation
|
# Additional set of commands which should not trigger schema generation
|
||||||
excluded_commands = [
|
if cmd_context['schema_excluded']:
|
||||||
'compilemessages',
|
|
||||||
'createsuperuser',
|
|
||||||
'clean_settings',
|
|
||||||
'collectstatic',
|
|
||||||
'makemessages',
|
|
||||||
'wait_for_db',
|
|
||||||
'list_apps',
|
|
||||||
'gunicorn',
|
|
||||||
'sqlflush',
|
|
||||||
'qcluster',
|
|
||||||
'check',
|
|
||||||
'shell',
|
|
||||||
'help',
|
|
||||||
]
|
|
||||||
|
|
||||||
if any(cmd in sys.argv for cmd in excluded_commands):
|
|
||||||
return _setGeneratingSchema(False)
|
return _setGeneratingSchema(False)
|
||||||
|
|
||||||
included_commands = [
|
if cmd_context['schema_generation']:
|
||||||
'schema',
|
|
||||||
'spectactular',
|
|
||||||
# schema adjacent calls
|
|
||||||
'export_settings_definitions',
|
|
||||||
'export_tags',
|
|
||||||
'export_filters',
|
|
||||||
'export_report_context',
|
|
||||||
]
|
|
||||||
|
|
||||||
if any(cmd in sys.argv for cmd in included_commands):
|
|
||||||
return _setGeneratingSchema(True)
|
return _setGeneratingSchema(True)
|
||||||
|
|
||||||
# This is a very inefficient call - so we only use it as a last resort
|
# This is a very inefficient call - so we only use it as a last resort
|
||||||
@@ -183,7 +247,7 @@ def isGeneratingSchema():
|
|||||||
|
|
||||||
def isInWorkerThread():
|
def isInWorkerThread():
|
||||||
"""Returns True if the current thread is a background worker thread."""
|
"""Returns True if the current thread is a background worker thread."""
|
||||||
return 'qcluster' in sys.argv
|
return cmd_context['worker']
|
||||||
|
|
||||||
|
|
||||||
def isInServerThread():
|
def isInServerThread():
|
||||||
@@ -191,10 +255,10 @@ def isInServerThread():
|
|||||||
if isInWorkerThread():
|
if isInWorkerThread():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if 'runserver' in sys.argv:
|
if cmd_context['runserver']:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return 'gunicorn' in sys.argv[0]
|
return cmd_context['gunicorn_entrypoint']
|
||||||
|
|
||||||
|
|
||||||
def isInMainThread():
|
def isInMainThread():
|
||||||
@@ -203,29 +267,12 @@ def isInMainThread():
|
|||||||
- The RUN_MAIN env is set in that case. However if --noreload is applied, this variable
|
- The RUN_MAIN env is set in that case. However if --noreload is applied, this variable
|
||||||
is not set because there are no different threads.
|
is not set because there are no different threads.
|
||||||
"""
|
"""
|
||||||
if 'runserver' in sys.argv and '--noreload' not in sys.argv:
|
if cmd_context['runserver'] and not cmd_context['noreload']:
|
||||||
return os.environ.get('RUN_MAIN', None) == 'true'
|
return os.environ.get('RUN_MAIN', None) == 'true'
|
||||||
|
|
||||||
return not isInWorkerThread()
|
return not isInWorkerThread()
|
||||||
|
|
||||||
|
|
||||||
def readOnlyCommands():
|
|
||||||
"""Return a list of read-only management commands which should not trigger database writes."""
|
|
||||||
return [
|
|
||||||
'help',
|
|
||||||
'check',
|
|
||||||
'shell',
|
|
||||||
'sqlflush',
|
|
||||||
'list_apps',
|
|
||||||
'wait_for_db',
|
|
||||||
'spectactular',
|
|
||||||
'makemessages',
|
|
||||||
'collectstatic',
|
|
||||||
'showmigrations',
|
|
||||||
'compilemessages',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def isReadOnlyCommand():
|
def isReadOnlyCommand():
|
||||||
"""Return True if the current command is a read-only command, which should not trigger any database writes."""
|
"""Return True if the current command is a read-only command, which should not trigger any database writes."""
|
||||||
if (
|
if (
|
||||||
@@ -236,7 +283,7 @@ def isReadOnlyCommand():
|
|||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return any(cmd in sys.argv for cmd in readOnlyCommands())
|
return cmd_context['read_only']
|
||||||
|
|
||||||
|
|
||||||
def canAppAccessDatabase(
|
def canAppAccessDatabase(
|
||||||
@@ -270,27 +317,19 @@ def canAppAccessDatabase(
|
|||||||
|
|
||||||
# If any of the following management commands are being executed,
|
# If any of the following management commands are being executed,
|
||||||
# prevent custom "on load" code from running!
|
# prevent custom "on load" code from running!
|
||||||
excluded_commands = [
|
if cmd_context['database_excluded']:
|
||||||
'compilemessages',
|
return False
|
||||||
'createsuperuser',
|
|
||||||
'collectstatic',
|
|
||||||
'makemessages',
|
|
||||||
'spectactular',
|
|
||||||
'wait_for_db',
|
|
||||||
'check',
|
|
||||||
]
|
|
||||||
|
|
||||||
if not allow_shell:
|
if not allow_shell and cmd_context['shell']:
|
||||||
excluded_commands.append('shell')
|
return False
|
||||||
|
|
||||||
if not allow_test:
|
if not allow_plugins and (
|
||||||
# Override for testing mode?
|
cmd_context['collect_plugins'] or cmd_context['list_apps']
|
||||||
excluded_commands.append('test')
|
):
|
||||||
|
return False
|
||||||
|
|
||||||
if not allow_plugins:
|
# Override for testing mode?
|
||||||
excluded_commands.extend(['collectplugins', 'list_apps'])
|
return allow_test or not cmd_context['test_command']
|
||||||
|
|
||||||
return all(cmd not in sys.argv for cmd in excluded_commands)
|
|
||||||
|
|
||||||
|
|
||||||
def isPluginRegistryLoaded():
|
def isPluginRegistryLoaded():
|
||||||
|
|||||||
Reference in New Issue
Block a user