Push remaining part of cleanup.py change

Now uses the location of the cleanup.py to find out where the basedir is, which seems more reliable.

Remaining problem: After deleting cherrypy, LL no longer starts cleanly. It gets to the right point, then fails because the import that was resolved earlier no longer works.

Restarting fixes it; it would be nice to fix.
This commit is contained in:
Allan Mertner 2023-01-17 01:15:15 +00:00
parent 59990bc4ed
commit 064e2d8f7b
3 changed files with 26 additions and 24 deletions

View File

@ -12,6 +12,7 @@ import shutil
import subprocess
import sys
from importlib.util import resolve_name
from pathlib import Path
from typing import Dict, List, Tuple
ll_dependencies = (
@ -38,24 +39,27 @@ ll_dependencies = (
def unbundle_libraries(dependencies: List[Tuple[str, str, str]] = ll_dependencies) -> List[str]:
""" Attempt to unbundle the dependencies passed.
""" Attempt to unbundle the dependencies passed, stored in subdirs off where basefile resides.
Returns a list of dependent libraries that were removed in the process.
Saves a file called unbundled.libs with these names; if this file exists, this routine does nothing.
"""
docker = '/config' in sys.argv and sys.argv[0].startswith('/app/')
bypass_file = os.path.join(os.getcwd(), 'unbundled.libs')
# This file must be in the lazylibrarian/ directory; the parent dir is the main one
basedir = str(Path(__file__).parent.parent.resolve())
bypass_file = os.path.join(basedir, 'unbundled.libs')
removed = []
if not docker and not os.path.isfile(bypass_file):
bundled, distro = get_library_locations(dependencies)
bundled, distro = get_library_locations(basedir, dependencies)
distro = install_missing_libraries(bundled, distro)
deletable = calc_libraries_to_delete(dependencies, distro)
removed = delete_libraries(deletable)
removed = delete_libraries(basedir, deletable)
with open(bypass_file, 'w') as f:
f.write(str(removed))
return removed
def get_library_locations(dependencies: List[Tuple[str, str, str]]) -> (str, Dict[str, str], Dict[str, str]):
def get_library_locations(basedir: str, dependencies: List[Tuple[str, str, str]]) -> (
str, Dict[str, str], Dict[str, str]):
""" Go through dependencies, return two dicts:
1) A dict of dependencies where we need to use the bundled version, and
2) a dict of dependencies where we can use a separate installation
@ -70,15 +74,17 @@ def get_library_locations(dependencies: List[Tuple[str, str, str]]) -> (str, Dic
if hasattr(finder, 'find_spec'):
spec = finder.find_spec(resolve_name(name, None), None)
if spec is not None:
if 'LazyLibrarian' in spec.origin:
if basedir in spec.origin:
bundled[name] = spec.origin
else:
distro[name] = spec.origin
curdir = paths.pop(0) # don't look in current working directory
# Look again, but not in the base LL directory where the bundles are
removed_base = False
for index, item in enumerate(paths):
if item == curdir:
paths.pop(index) # Remove curdir if it occurs multiple times!
if item == basedir:
paths.pop(index)
removed_base = True
try:
for item in dependencies:
name = item[2] if item[2] else item[0]
@ -90,7 +96,8 @@ def get_library_locations(dependencies: List[Tuple[str, str, str]]) -> (str, Dic
distro[name] = spec.origin
break
finally:
sys.path.insert(0, curdir)
if removed_base:
sys.path.insert(0, basedir)
return bundled, distro
@ -122,13 +129,11 @@ def calc_libraries_to_delete(dependencies: List[Tuple[str, str, str]], distro: D
return deletable
def delete_libraries(deletable: List[str]) -> List[str]:
def delete_libraries(basedir: str, deletable: List[str]) -> List[str]:
""" Delete all of the libraries in deletable, return a list of those deleted """
removed = []
# All of the libraries are off the "root" of lazylibrarian
cwd = os.getcwd()
for item in deletable:
f = os.path.join(cwd, item)
f = os.path.join(basedir, item)
# might have already been deleted
if os.path.isdir(f):
shutil.rmtree(f)

View File

@ -205,9 +205,6 @@ class StartupLazyLibrarian:
config.post_load_fixup()
DIRS.ensure_log_dir()
if options.nolaunch:
config.set_bool('LAUNCH_BROWSER', False)
def init_loggers(self, console_only: bool):
""" Initialize log files. Until this is done, do not use the logger """
if console_only:

View File

@ -19,9 +19,9 @@ class CleanupTest(TestCase):
def test_get_library_locations(self):
# Validate that we know this library isn't bundled
lib = [('apprise', '', '')]
bundled, distro = get_library_locations(lib)
bundled, distro = get_library_locations(basedir='/test', dependencies=lib)
self.assertTrue(lib[0][0] not in bundled)
self.assertLessEqual(1, len(distro), 'Expect to find at most 1 item in distro')
self.assertLessEqual(len(distro), 1, 'Expect to find at most 1 item in distro')
if distro:
self.assertTrue(lib[0][0] in distro, 'Expect to find the key for the lib we looked for')
@ -76,16 +76,16 @@ class CleanupTest(TestCase):
def test_delete_libraries(self, mock_shutil_rmtree, mock_os_path_isfile, mock_os_path_isdir, mock_os_remove):
cwd = os.getcwd()
libraries = []
removed = delete_libraries(libraries)
removed = delete_libraries('', libraries)
self.assertEqual([], removed, 'We removed libraries when none were supplied')
# Test removing a file
mock_os_path_isdir.return_value = False
mock_os_path_isfile.return_value = True
libraries = ['somefile']
removed = delete_libraries(libraries)
removed = delete_libraries('/test', libraries)
self.assertEqual(['somefile'], removed, 'Expected to remove the file supplied')
expect = os.path.join(cwd, 'somefile')
expect = os.path.join('/test', 'somefile')
mock_os_path_isdir.assert_called_with(expect)
mock_os_remove.assert_called_with(expect)
@ -93,8 +93,8 @@ class CleanupTest(TestCase):
mock_os_path_isdir.return_value = True
mock_os_path_isfile.return_value = False
libraries = ['somedir']
removed = delete_libraries(libraries)
removed = delete_libraries('/test', libraries)
self.assertEqual(['somedir'], removed, 'Expected to remove the dir supplied')
expect = os.path.join(cwd, 'somedir')
expect = os.path.join('/test', 'somedir')
mock_os_path_isdir.assert_called_with(expect)
mock_shutil_rmtree.assert_called_with(expect)