Skip to content

Commit

Permalink
handle installer exceptions and update requirements
Browse files Browse the repository at this point in the history
  • Loading branch information
vladmandic committed Feb 22, 2024
1 parent 9c12b74 commit e64055c
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 126 deletions.
119 changes: 75 additions & 44 deletions installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
import platform
import subprocess
import cProfile
import pkg_resources

try:
import pkg_resources # python 3.12 no longer packages it built-in
except ImportError:
stdout = subprocess.run(f'"{sys.executable}" -m pip install setuptools', shell=True, check=False, env=os.environ, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
import pkg_resources


class Dot(dict): # dot notation access to dictionary attributes
Expand Down Expand Up @@ -163,8 +168,11 @@ def installed(package, friendly: str = None, reload = False, quiet = False):
ok = True
try:
if reload:
import imp # pylint: disable=deprecated-module
imp.reload(pkg_resources)
try:
import imp # pylint: disable=deprecated-module
imp.reload(pkg_resources)
except Exception:
pass
if friendly:
pkgs = friendly.split()
else:
Expand Down Expand Up @@ -201,10 +209,15 @@ def installed(package, friendly: str = None, reload = False, quiet = False):
return False


def uninstall(package):
if installed(package, package, quiet=True):
log.warning(f'Uninstalling: {package}')
pip(f"uninstall {package} --yes --quiet", ignore=True, quiet=True)
def uninstall(package, quiet = False):
packages = package if isinstance(package, list) else [package]
res = ''
for p in packages:
if installed(p, p, quiet=True):
if not quiet:
log.warning(f'Uninstalling: {p}')
res += pip(f"uninstall {p} --yes --quiet", ignore=True, quiet=True)
return res


def pip(arg: str, ignore: bool = False, quiet: bool = False):
Expand All @@ -229,11 +242,18 @@ def pip(arg: str, ignore: bool = False, quiet: bool = False):

# install package using pip if not already installed
def install(package, friendly: str = None, ignore: bool = False):
res = ''
if args.reinstall or args.upgrade:
global quick_allowed # pylint: disable=global-statement
quick_allowed = False
if args.reinstall or not installed(package, friendly):
pip(f"install --upgrade {package}", ignore=ignore)
res = pip(f"install --upgrade {package}", ignore=ignore)
try:
import imp # pylint: disable=deprecated-module
imp.reload(pkg_resources)
except Exception:
pass
return res


# execute git command
Expand Down Expand Up @@ -362,6 +382,12 @@ def check_python():
log.debug(f'Git {git_version.replace("git version", "").strip()}')


# check onnx version
def check_onnx():
if not installed('onnxruntime', quiet=True) and not installed('onnxruntime-gpu', quiet=True): # allow either
install('onnxruntime', 'onnxruntime', ignore=True)


# check torch version
def check_torch():
if args.skip_torch:
Expand All @@ -379,8 +405,13 @@ def check_torch():
log.debug(f'Torch allowed: cuda={allow_cuda} rocm={allow_rocm} ipex={allow_ipex} diml={allow_directml} openvino={allow_openvino}')
torch_command = os.environ.get('TORCH_COMMAND', '')
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'none')
if not installed('onnxruntime', quiet=True) and not installed('onnxruntime-gpu', quiet=True): # allow either
install('onnxruntime', 'onnxruntime', ignore=True)
def is_rocm_available():
if not allow_rocm:
return False
if platform.system() == 'Windows':
hip_path = os.environ.get('HIP_PATH', None)
return hip_path is not None and os.path.exists(os.path.join(hip_path, 'bin'))
return shutil.which('rocminfo') is not None or os.path.exists('/opt/rocm/bin/rocminfo') or os.path.exists('/dev/kfd')
if torch_command != '':
pass
elif allow_cuda and (shutil.which('nvidia-smi') is not None or args.use_xformers or os.path.exists(os.path.join(os.environ.get('SystemRoot') or r'C:\Windows', 'System32', 'nvidia-smi.exe'))):
Expand All @@ -391,14 +422,21 @@ def check_torch():
torch_command = os.environ.get('TORCH_COMMAND', 'torch torchvision --index-url https://download.pytorch.org/whl/cu118')
xformers_package = os.environ.get('XFORMERS_PACKAGE', '--pre xformers' if opts.get('cross_attention_optimization', '') == 'xFormers' else 'none')
install('onnxruntime-gpu', 'onnxruntime-gpu', ignore=True)
elif allow_rocm and (shutil.which('rocminfo') is not None or os.path.exists('/opt/rocm/bin/rocminfo') or os.path.exists('/dev/kfd')):
elif is_rocm_available():
is_windows = platform.system() == 'Windows' # provides more better logs for ZLUDA users and ROCm for Windows users in future.
log.info('AMD ROCm toolkit detected')
os.environ.setdefault('PYTORCH_HIP_ALLOC_CONF', 'garbage_collection_threshold:0.8,max_split_size_mb:512')
os.environ.setdefault('TENSORFLOW_PACKAGE', 'tensorflow-rocm')
if not is_windows:
os.environ.setdefault('TENSORFLOW_PACKAGE', 'tensorflow-rocm')
try:
command = subprocess.run('rocm_agent_enumerator', shell=True, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
amd_gpus = command.stdout.decode(encoding="utf8", errors="ignore").split('\n')
amd_gpus = [x for x in amd_gpus if x and x != 'gfx000']
if is_windows:
command = subprocess.run('hipinfo', shell=True, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
amd_gpus = command.stdout.decode(encoding="utf8", errors="ignore").split('\n')
amd_gpus = [x.split(' ')[-1].strip() for x in amd_gpus if x.startswith('gcnArchName:')]
else:
command = subprocess.run('rocm_agent_enumerator', shell=True, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
amd_gpus = command.stdout.decode(encoding="utf8", errors="ignore").split('\n')
amd_gpus = [x for x in amd_gpus if x and x != 'gfx000']
log.debug(f'ROCm agents detected: {amd_gpus}')
except Exception as e:
log.debug(f'Run rocm_agent_enumerator failed: {e}')
Expand Down Expand Up @@ -433,22 +471,17 @@ def check_torch():
except Exception as e:
log.debug(f'ROCm hipconfig failed: {e}')
rocm_ver = None
if rocm_ver in {"5.7"}:
torch_command = os.environ.get('TORCH_COMMAND', f'torch torchvision --pre --index-url https://download.pytorch.org/whl/nightly/rocm{rocm_ver}')
elif rocm_ver in {"5.5", "5.6"}:
torch_command = os.environ.get('TORCH_COMMAND', f'torch torchvision --index-url https://download.pytorch.org/whl/nightly/rocm{rocm_ver}')
else:
# ROCm 5.5 is oldest for PyTorch 2.1
torch_command = os.environ.get('TORCH_COMMAND', 'torch torchvision --index-url https://download.pytorch.org/whl/rocm5.5')
if not is_windows: # remove after PyTorch built with ROCm for Windows is launched
if rocm_ver in {"5.7"}:
torch_command = os.environ.get('TORCH_COMMAND', f'torch torchvision --pre --index-url https://download.pytorch.org/whl/nightly/rocm{rocm_ver}')
elif rocm_ver in {"5.5", "5.6"}:
torch_command = os.environ.get('TORCH_COMMAND', f'torch torchvision --index-url https://download.pytorch.org/whl/nightly/rocm{rocm_ver}')
else:
# ROCm 5.5 is oldest for PyTorch 2.1
torch_command = os.environ.get('TORCH_COMMAND', 'torch torchvision --index-url https://download.pytorch.org/whl/rocm5.5')
if rocm_ver is not None:
install(os.environ.get('ONNXRUNTIME_PACKAGE', get_onnxruntime_source_for_rocm(arr)), "onnxruntime-training built with ROCm", ignore=True)
xformers_package = os.environ.get('XFORMERS_PACKAGE', 'none')
if rocm_ver is not None:
install(os.environ.get('ONNXRUNTIME_PACKAGE', get_onnxruntime_source_for_rocm(arr)), "onnxruntime-training built with ROCm", ignore=True)
try:
import onnxruntime
if "ROCMExecutionProvider" not in onnxruntime.get_available_providers():
log.warning('Failed to automatically install onxnruntime package for ROCm. Please manually install it if you need.')
except Exception:
pass
elif allow_ipex and (args.use_ipex or shutil.which('sycl-ls') is not None or shutil.which('sycl-ls.exe') is not None or os.environ.get('ONEAPI_ROOT') is not None or os.path.exists('/opt/intel/oneapi') or os.path.exists("C:/Program Files (x86)/Intel/oneAPI") or os.path.exists("C:/oneAPI")):
args.use_ipex = True # pylint: disable=attribute-defined-outside-init
log.info('Intel OneAPI Toolkit detected')
Expand Down Expand Up @@ -482,10 +515,10 @@ def check_torch():
install('onnxruntime-openvino', 'onnxruntime-openvino', ignore=True)
elif allow_openvino and args.use_openvino:
log.info('Using OpenVINO')
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.1.2 torchvision==0.16.2 --index-url https://download.pytorch.org/whl/cpu')
torch_command = os.environ.get('TORCH_COMMAND', 'torch==2.2.0 torchvision==0.17.0 --index-url https://download.pytorch.org/whl/cpu')
install(os.environ.get('OPENVINO_PACKAGE', 'openvino==2023.3.0'), 'openvino')
install('onnxruntime-openvino', 'onnxruntime-openvino', ignore=True) # TODO openvino: numpy version conflicts with tensorflow and doesn't support Python 3.11
install('nncf==2.8.0', 'nncf')
install('nncf==2.8.1', 'nncf')
os.environ.setdefault('PYTORCH_TRACING_MODE', 'TORCHFX')
os.environ.setdefault('NEOReadDebugKeys', '1')
os.environ.setdefault('ClDeviceGlobalMemSizeAvailablePercent', '100')
Expand Down Expand Up @@ -555,6 +588,8 @@ def check_torch():
log.debug(f'Cannot install xformers package: {e}')
if opts.get('cuda_compile_backend', '') == 'hidet':
install('hidet', 'hidet')
if opts.get('cuda_compile_backend', '') == 'deep-cache':
install('DeepCache')
if opts.get('nncf_compress_weights', False) and not args.use_openvino:
install('nncf==2.7.0', 'nncf')
if args.profile:
Expand Down Expand Up @@ -613,10 +648,11 @@ def run_extension_installer(folder):
env = os.environ.copy()
env['PYTHONPATH'] = os.path.abspath(".")
result = subprocess.run(f'"{sys.executable}" "{path_installer}"', shell=True, env=env, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=folder)
txt = result.stdout.decode(encoding="utf8", errors="ignore")
debug(f'Extension installer: file={path_installer} {txt}')
if result.returncode != 0:
global errors # pylint: disable=global-statement
errors += 1
txt = result.stdout.decode(encoding="utf8", errors="ignore")
if len(result.stderr) > 0:
txt = txt + '\n' + result.stderr.decode(encoding="utf8", errors="ignore")
log.error(f'Error running extension installer: {path_installer}')
Expand Down Expand Up @@ -843,21 +879,15 @@ def get_version():


def get_onnxruntime_source_for_rocm(rocm_ver):
ort_version = "1.16.3"

try:
import onnxruntime
ort_version = onnxruntime.__version__
except ImportError:
pass

ort_version = "1.16.3" # hardcoded
cp_str = f"{sys.version_info.major}{sys.version_info.minor}"

if rocm_ver is None:
command = subprocess.run('hipconfig --version', shell=True, check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
rocm_ver = command.stdout.decode(encoding="utf8", errors="ignore").split('.')

return f"https://download.onnxruntime.ai/onnxruntime_training-{ort_version}%2Brocm{rocm_ver[0]}{rocm_ver[1]}-cp{cp_str}-cp{cp_str}-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
if "linux" in sys.platform:
return f"https://download.onnxruntime.ai/onnxruntime_training-{ort_version}%2Brocm{rocm_ver[0]}{rocm_ver[1]}-cp{cp_str}-cp{cp_str}-manylinux_2_17_x86_64.manylinux2014_x86_64.whl"
else:
return 'onnxruntime-gpu'


# check version of the main repo and optionally upgrade it
Expand Down Expand Up @@ -971,6 +1001,7 @@ def add_args(parser):
group.add_argument('--skip-git', default = os.environ.get("SD_SKIPGIT",False), action='store_true', help = "Skips running all GIT operations, default: %(default)s")
group.add_argument('--skip-torch', default = os.environ.get("SD_SKIPTORCH",False), action='store_true', help = "Skips running Torch checks, default: %(default)s")
group.add_argument('--skip-all', default = os.environ.get("SD_SKIPALL",False), action='store_true', help = "Skips running all checks, default: %(default)s")
group.add_argument('--skip-env', default = os.environ.get("SD_SKIPENV",False), action='store_true', help = "Skips setting of env variables during startup, default: %(default)s")
group.add_argument('--experimental', default = os.environ.get("SD_EXPERIMENTAL",False), action='store_true', help = "Allow unsupported versions of libraries, default: %(default)s")
group.add_argument('--reinstall', default = os.environ.get("SD_REINSTALL",False), action='store_true', help = "Force reinstallation of all requirements, default: %(default)s")
group.add_argument('--test', default = os.environ.get("SD_TEST",False), action='store_true', help = "Run test only and exit")
Expand Down
27 changes: 2 additions & 25 deletions modules/generation_parameters_copypaste.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
from PIL import Image
import gradio as gr
from modules.paths import data_path
from modules import shared, ui_tempdir, script_callbacks, images
from modules import shared, gr_tempdir, script_callbacks, images


re_param_code = r'\s*([\w ]+):\s*("(?:\\"[^,]|\\"|\\|[^\"])+"|[^,]*)(?:,|$)'
re_param = re.compile(re_param_code)
re_imagesize = re.compile(r"^(\d+)x(\d+)$")
re_hypernet_hash = re.compile("\(([0-9a-f]+)\)$") # pylint: disable=anomalous-backslash-in-string
type_of_gr_update = type(gr.update())
paste_fields = {}
registered_param_bindings = []
Expand Down Expand Up @@ -58,7 +57,7 @@ def image_from_url_text(filedata):
filedata = filedata[0]
if type(filedata) == dict and filedata.get("is_file", False):
filename = filedata["name"]
is_in_right_dir = ui_tempdir.check_tmp_file(shared.demo, filename)
is_in_right_dir = gr_tempdir.check_tmp_file(shared.demo, filename)
if is_in_right_dir:
filename = filename.rsplit('?', 1)[0]
if not os.path.exists(filename):
Expand Down Expand Up @@ -188,28 +187,6 @@ def send_image_and_dimensions(x):
return img, w, h


def find_hypernetwork_key(hypernet_name, hypernet_hash=None):
"""Determines the config parameter name to use for the hypernet based on the parameters in the infotext.
Example: an infotext provides "Hypernet: ke-ta" and "Hypernet hash: 1234abcd". For the "Hypernet" config
parameter this means there should be an entry that looks like "ke-ta-10000(1234abcd)" to set it to.
If the infotext has no hash, then a hypernet with the same name will be selected instead.
"""
hypernet_name = hypernet_name.lower()
if hypernet_hash is not None:
# Try to match the hash in the name
for hypernet_key in shared.hypernetworks.keys():
result = re_hypernet_hash.search(hypernet_key)
if result is not None and result[1] == hypernet_hash:
return hypernet_key
else:
# Fall back to a hypernet with the same name
for hypernet_key in shared.hypernetworks.keys():
if hypernet_key.lower().startswith(hypernet_name):
return hypernet_key

return None


def parse_generation_parameters(x: str):
res = {}
if x is None:
Expand Down
99 changes: 99 additions & 0 deletions modules/gr_tempdir.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import os
import tempfile
from collections import namedtuple
from pathlib import Path
from PIL import Image, PngImagePlugin
from modules import shared, errors, paths


Savedfile = namedtuple("Savedfile", ["name"])
debug = errors.log.trace if os.environ.get('SD_PATH_DEBUG', None) is not None else lambda *args, **kwargs: None


def register_tmp_file(gradio, filename):
if hasattr(gradio, 'temp_file_sets'):
gradio.temp_file_sets[0] = gradio.temp_file_sets[0] | {os.path.abspath(filename)}


def check_tmp_file(gradio, filename):
ok = False
if hasattr(gradio, 'temp_file_sets'):
ok = ok or any(filename in fileset for fileset in gradio.temp_file_sets)
if shared.opts.outdir_samples != '':
ok = ok or Path(shared.opts.outdir_samples).resolve() in Path(filename).resolve().parents
else:
ok = ok or Path(shared.opts.outdir_txt2img_samples).resolve() in Path(filename).resolve().parents
ok = ok or Path(shared.opts.outdir_img2img_samples).resolve() in Path(filename).resolve().parents
ok = ok or Path(shared.opts.outdir_extras_samples).resolve() in Path(filename).resolve().parents
if shared.opts.outdir_grids != '':
ok = ok or Path(shared.opts.outdir_grids).resolve() in Path(filename).resolve().parents
else:
ok = ok or Path(shared.opts.outdir_txt2img_grids).resolve() in Path(filename).resolve().parents
ok = ok or Path(shared.opts.outdir_img2img_grids).resolve() in Path(filename).resolve().parents
ok = ok or Path(shared.opts.outdir_save).resolve() in Path(filename).resolve().parents
ok = ok or Path(shared.opts.outdir_init_images).resolve() in Path(filename).resolve().parents
return ok


def pil_to_temp_file(self, img: Image, dir: str, format="png") -> str: # pylint: disable=redefined-builtin,unused-argument
"""
# original gradio implementation
bytes_data = gr.processing_utils.encode_pil_to_bytes(img, format)
temp_dir = Path(dir) / self.hash_bytes(bytes_data)
temp_dir.mkdir(exist_ok=True, parents=True)
filename = str(temp_dir / f"image.{format}")
img.save(filename, pnginfo=gr.processing_utils.get_pil_metadata(img))
"""
folder = dir
already_saved_as = getattr(img, 'already_saved_as', None)
exists = os.path.isfile(already_saved_as) if already_saved_as is not None else False
debug(f'Image lookup: {already_saved_as} exists={exists}')
if already_saved_as and exists:
register_tmp_file(shared.demo, already_saved_as)
file_obj = Savedfile(already_saved_as)
name = file_obj.name
debug(f'Image registered: {name}')
return name
if shared.opts.temp_dir != "":
folder = shared.opts.temp_dir
use_metadata = False
metadata = PngImagePlugin.PngInfo()
for key, value in img.info.items():
if isinstance(key, str) and isinstance(value, str):
metadata.add_text(key, value)
use_metadata = True
if not os.path.exists(folder):
os.makedirs(folder, exist_ok=True)
shared.log.debug(f'Created temp folder: path="{folder}"')
with tempfile.NamedTemporaryFile(delete=False, suffix=".png", dir=folder) as tmp:
name = tmp.name
img.save(name, pnginfo=(metadata if use_metadata else None))
img.already_saved_as = name
size = os.path.getsize(name)
shared.log.debug(f'Saving temp: image="{name}" resolution={img.width}x{img.height} size={size}')
params = ', '.join([f'{k}: {v}' for k, v in img.info.items()])
params = params[12:] if params.startswith('parameters: ') else params
with open(os.path.join(paths.data_path, "params.txt"), "w", encoding="utf8") as file:
file.write(params)
return name


# override save to file function so that it also writes PNG info

def on_tmpdir_changed():
if shared.opts.temp_dir == "":
return
register_tmp_file(shared.demo, os.path.join(shared.opts.temp_dir, "x"))


def cleanup_tmpdr():
temp_dir = shared.opts.temp_dir
if temp_dir == "" or not os.path.isdir(temp_dir):
return
for root, _dirs, files in os.walk(temp_dir, topdown=False):
for name in files:
_, extension = os.path.splitext(name)
if extension != ".png" and extension != ".jpg" and extension != ".webp":
continue
filename = os.path.join(root, name)
os.remove(filename)
Loading

0 comments on commit e64055c

Please sign in to comment.