diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index c0007b6604..e3ee56ba9c 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -1,24 +1,19 @@ # -*- coding: utf-8 -*- -"""Bootstrap Pype repositories. - -Attrbutes: - data_dir (str): platform dependent path where pype expects its - repositories and configuration files. -""" +"""Bootstrap Pype repositories.""" import sys import os import re import logging as log import shutil import tempfile -from typing import Union, Callable +from typing import Union, Callable, Dict from zipfile import ZipFile from appdirs import user_data_dir from pype.version import __version__ -class BootstrapRepos(): +class BootstrapRepos: """Class for bootstrapping local Pype installation. Attributes: @@ -46,7 +41,8 @@ class BootstrapRepos(): ) ) - def get_local_version(self) -> str: + @staticmethod + def get_local_version() -> str: """Get version of local Pype.""" return __version__ @@ -82,7 +78,7 @@ class BootstrapRepos(): ) self._log.info(f"creating zip: {temp_zip}") - self._create_pype_zip( + BootstrapRepos._create_pype_zip( temp_zip, repo_dir, progress_callback=progress_callback) if not os.path.exists(temp_zip): self._log.error("make archive failed.") @@ -106,12 +102,13 @@ class BootstrapRepos(): return None return os.path.join(self.data_dir, os.path.basename(temp_zip)) + @staticmethod def _create_pype_zip( - self, zip_path: str, dir: str, + zip_path: str, include_dir: str, progress_callback: Callable, include_pype: bool = True) -> None: """Pack repositories and Pype into zip. - We are using `zipfile` instead :meth:`shutil.make_archive()` to later + We are using `zipfile` instead :meth:`shutil.make_archive` to later implement file filter to skip git related stuff to make it into archive. @@ -120,12 +117,14 @@ class BootstrapRepos(): Args: zip_path (str): path to zip file. - dir: repo directories to inlcude. + include_dir: repo directories to include. progress_callback (Callable): callback to report progress back to UI progress bar. - include_pype (bool): add Pype module itelf. + include_pype (bool): add Pype module itself. """ - repo_files = sum(len(files) for _, _, files in os.walk(dir)) + repo_files = sum(len(files) for _, _, files in os.walk(include_dir)) + assert repo_files != 0, f"No repositories to include in {include_dir}" + pype_inc = 0 if include_pype: pype_files = sum(len(files) for _, _, files in os.walk('pype')) repo_inc = 48.0 / float(repo_files) @@ -134,13 +133,13 @@ class BootstrapRepos(): repo_inc = 98.0 / float(repo_files) progress = 0 with ZipFile(zip_path, "w") as zip: - for root, _, files in os.walk(dir): + for root, _, files in os.walk(include_dir): for file in files: zip.write( os.path.relpath(os.path.join(root, file), - os.path.join(dir, '..')), + os.path.join(include_dir, '..')), os.path.relpath(os.path.join(root, file), - os.path.join(dir)) + os.path.join(include_dir)) ) progress += repo_inc progress_callback(int(progress)) @@ -161,7 +160,8 @@ class BootstrapRepos(): zip.testzip() progress_callback(100) - def add_paths_from_archive(self, archive: str) -> None: + @staticmethod + def add_paths_from_archive(archive: str) -> None: """Add first-level directories as paths to sys.path. This will enable Python to import modules is second-level directories @@ -172,8 +172,8 @@ class BootstrapRepos(): """ name_list = [] - with ZipFile(archive, "r") as zip: - name_list = zip.namelist() + with ZipFile(archive, "r") as zip_file: + name_list = zip_file.namelist() roots = [] for item in name_list: @@ -188,7 +188,7 @@ class BootstrapRepos(): os.environ["PYTHONPATH"] = os.pathsep.join(paths) - def find_pype(self) -> Union[str, None]: + def find_pype(self) -> Union[Dict, None]: """Get ordered dict of detected Pype version. Returns: diff --git a/pype/lib/__init__.py b/pype/lib/__init__.py index a9dff8fd61..545214612c 100644 --- a/pype/lib/__init__.py +++ b/pype/lib/__init__.py @@ -18,6 +18,10 @@ from .mongo import ( get_default_components ) +from .user_settings import IniSettingRegistry +from .user_settings import JSONSettingRegistry +from .user_settings import PypeSettingsRegistry + terminal = Terminal __all__ = [ @@ -33,5 +37,7 @@ __all__ = [ PypeLogger, decompose_url, compose_url, - get_default_components + get_default_components, + IniSettingRegistry, + JSONSettingRegistry ] diff --git a/requirements.txt b/requirements.txt index a523a2df88..9f8bcccfba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,3 +22,4 @@ Pillow log4mongo cx_Freeze jsonschema +keyring diff --git a/setup.py b/setup.py index 756a132251..83811de063 100644 --- a/setup.py +++ b/setup.py @@ -13,6 +13,7 @@ __version__ = version['__version__'] install_requires = [ "appdirs", "cx_Freeze", + "keyring", "clique", "jsonschema", "OpenTimelineIO", diff --git a/tests/igniter/test_bootstrap_repos.py b/tests/igniter/test_bootstrap_repos.py index 81581294ad..0d0d7a61fe 100644 --- a/tests/igniter/test_bootstrap_repos.py +++ b/tests/igniter/test_bootstrap_repos.py @@ -7,15 +7,15 @@ from igniter.bootstrap_repos import BootstrapRepos @pytest.fixture -def fix_bootrap(tmp_path): +def fix_bootstrap(tmp_path): bs = BootstrapRepos() bs.live_repo_dir = os.path.abspath('repos') bs.data_dir = tmp_path return bs -def test_install_live_repos(fix_bootrap, printer): - rf = fix_bootrap.install_live_repos() +def test_install_live_repos(fix_bootstrap, printer): + rf = fix_bootstrap.install_live_repos() sep = os.path.sep expected_paths = [ f"{rf}{sep}acre", @@ -28,7 +28,7 @@ def test_install_live_repos(fix_bootrap, printer): ] printer("testing zip creation") assert os.path.exists(rf), "zip archive was not created" - fix_bootrap.add_paths_from_archive(rf) + fix_bootstrap.add_paths_from_archive(rf) for ep in expected_paths: assert ep in sys.path, f"{ep} not set correctly" @@ -43,7 +43,7 @@ def test_install_live_repos(fix_bootrap, printer): f"{rf}{sep}pype{sep}pype{sep}__init__.py" -def test_find_pype(fix_bootrap): +def test_find_pype(fix_bootstrap): test_versions = [ "pype-repositories-v3.0.0.zip", "pype-repositories-v3.0.1.zip", @@ -52,12 +52,12 @@ def test_find_pype(fix_bootrap): "pype-repositories-v3.2.0.zip", ] for test_file in test_versions: - with open(os.path.join(fix_bootrap.data_dir, test_file), "w") as fp: + with open(os.path.join(fix_bootstrap.data_dir, test_file), "w") as fp: fp.write(test_file) - result = fix_bootrap.find_pype() + result = fix_bootstrap.find_pype() # we should have results as file were created assert result is not None, "no Pype version found" # latest item in `result` should be latest version found. assert list(result.values())[-1] == os.path.join( - fix_bootrap.data_dir, test_versions[3]), "not a latest version of Pype" + fix_bootstrap.data_dir, test_versions[3]), "not a latest version of Pype" diff --git a/tests/pype/lib/test_user_settings.py b/tests/pype/lib/test_user_settings.py new file mode 100644 index 0000000000..32de58eb07 --- /dev/null +++ b/tests/pype/lib/test_user_settings.py @@ -0,0 +1,88 @@ +import pytest +from pype.lib import IniSettingRegistry +from pype.lib import JSONSettingRegistry +from pype.lib import PypeSettingsRegistry +from uuid import uuid4 +import configparser + + +@pytest.fixture +def json_registry(tmpdir): + name = "pypetest_{}".format(str(uuid4())) + r = JSONSettingRegistry(name, tmpdir) + yield r + + +@pytest.fixture +def ini_registry(tmpdir): + name = "pypetest_{}".format(str(uuid4())) + r = IniSettingRegistry(name, tmpdir) + yield r + + +def test_keyring(json_registry, printer): + printer("testing get/set") + service = json_registry._name + json_registry.set_secure_item("item1", "foo") + json_registry.set_secure_item("item2", "bar") + result1 = json_registry.get_secure_item("item1") + result2 = json_registry.get_secure_item("item2") + + assert result1 == "foo" + assert result2 == "bar" + + printer(f"testing delete from {service}") + + json_registry.delete_secure_item("item1") + json_registry.delete_secure_item("item2") + + with pytest.raises(ValueError): + json_registry.get_secure_item("item1") + json_registry.get_secure_item("item2") + + +def test_ini_registry(ini_registry, printer): + printer("testing get/set") + ini_registry.set_item("test1", "bar") + ini_registry.set_item_section("TEST", "test2", "foo") + ini_registry.set_item_section("TEST", "test3", "baz") + + result1 = ini_registry.get_item("test1") + result2 = ini_registry.get_item_from_section("TEST", "test2") + result3 = ini_registry.get_item_from_section("TEST", "test3") + + assert result1 == "bar" + assert result2 == "foo" + assert result3 == "baz" + + printer("test non-existent value") + with pytest.raises(ValueError): + ini_registry.get_item("xxx") + + with pytest.raises(ValueError): + ini_registry.get_item_from_section("FFF", "yyy") + + printer("test deleting") + + ini_registry.delete_item("test1") + with pytest.raises(ValueError): + ini_registry.get_item("test1") + + ini_registry.delete_item_from_section("TEST", "test2") + with pytest.raises(ValueError): + ini_registry.get_item_from_section("TEST", "test2") + + ini_registry.delete_item_from_section("TEST", "test3") + with pytest.raises(ValueError): + ini_registry.get_item_from_section("TEST", "test3") + + # ensure TEST section is also deleted + cfg = configparser.ConfigParser() + cfg.read(ini_registry._registry_file) + assert "TEST" not in cfg.sections() + + with pytest.raises(ValueError): + ini_registry.delete_item("ooo") + + with pytest.raises(ValueError): + ini_registry.delete_item_from_section("XXX", "UUU")