mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into feature/1004-deadline-better-handling-of-pype
This commit is contained in:
commit
7c4aa85de0
43 changed files with 2737 additions and 1660 deletions
|
|
@ -2,26 +2,38 @@
|
|||
"""Open install dialog."""
|
||||
|
||||
import sys
|
||||
from Qt import QtWidgets # noqa
|
||||
from Qt.QtCore import Signal # noqa
|
||||
import os
|
||||
os.chdir(os.path.dirname(__file__)) # for override sys.path in Deadline
|
||||
|
||||
from Qt import QtWidgets
|
||||
|
||||
from .install_dialog import InstallDialog
|
||||
from .bootstrap_repos import BootstrapRepos
|
||||
from .version import __version__ as version
|
||||
|
||||
|
||||
def run():
|
||||
RESULT = 0
|
||||
|
||||
|
||||
def get_result(res: int):
|
||||
"""Sets result returned from dialog."""
|
||||
global RESULT
|
||||
RESULT = res
|
||||
|
||||
|
||||
def open_dialog():
|
||||
"""Show Igniter dialog."""
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
d = InstallDialog()
|
||||
d.exec_()
|
||||
d.show()
|
||||
sys.exit(app.exec_())
|
||||
d.finished.connect(get_result)
|
||||
d.open()
|
||||
app.exec()
|
||||
return RESULT
|
||||
|
||||
|
||||
__all__ = [
|
||||
"InstallDialog",
|
||||
"BootstrapRepos",
|
||||
"run"
|
||||
"open_dialog",
|
||||
"version"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,11 +2,25 @@
|
|||
"""Open install dialog."""
|
||||
|
||||
import sys
|
||||
from Qt import QtWidgets
|
||||
from Qt import QtWidgets # noqa
|
||||
from Qt.QtCore import Signal # noqa
|
||||
|
||||
from .install_dialog import InstallDialog
|
||||
|
||||
|
||||
RESULT = 0
|
||||
|
||||
|
||||
def get_result(res: int):
|
||||
"""Sets result returned from dialog."""
|
||||
global RESULT
|
||||
RESULT = res
|
||||
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
d = InstallDialog()
|
||||
d.show()
|
||||
sys.exit(app.exec_())
|
||||
d.finished.connect(get_result)
|
||||
d.open()
|
||||
app.exec()
|
||||
sys.exit(RESULT)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,12 @@ from appdirs import user_data_dir
|
|||
from speedcopy import copyfile
|
||||
|
||||
from .user_settings import PypeSettingsRegistry
|
||||
from .tools import load_environments
|
||||
from .tools import get_pype_path_from_db
|
||||
|
||||
|
||||
LOG_INFO = 0
|
||||
LOG_WARNING = 1
|
||||
LOG_ERROR = 3
|
||||
|
||||
|
||||
@functools.total_ordering
|
||||
|
|
@ -285,6 +290,9 @@ class BootstrapRepos:
|
|||
def get_version(repo_dir: Path) -> Union[str, None]:
|
||||
"""Get version of Pype in given directory.
|
||||
|
||||
Note: in frozen Pype installed in user data dir, this must point
|
||||
one level deeper as it is `pype-version-v3.0.0/pype/pype/version.py`
|
||||
|
||||
Args:
|
||||
repo_dir (Path): Path to Pype repo.
|
||||
|
||||
|
|
@ -304,7 +312,8 @@ class BootstrapRepos:
|
|||
|
||||
return version['__version__']
|
||||
|
||||
def install_live_repos(self, repo_dir: Path = None) -> Union[Path, None]:
|
||||
def create_version_from_live_code(
|
||||
self, repo_dir: Path = None) -> Union[PypeVersion, None]:
|
||||
"""Copy zip created from Pype repositories to user data dir.
|
||||
|
||||
This detect Pype version either in local "live" Pype repository
|
||||
|
|
@ -328,6 +337,10 @@ class BootstrapRepos:
|
|||
else:
|
||||
version = self.get_version(repo_dir)
|
||||
|
||||
if not version:
|
||||
self._print("Pype not found.", LOG_ERROR)
|
||||
return
|
||||
|
||||
# create destination directory
|
||||
if not self.data_dir.exists():
|
||||
self.data_dir.mkdir(parents=True)
|
||||
|
|
@ -336,30 +349,127 @@ class BootstrapRepos:
|
|||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
temp_zip = \
|
||||
Path(temp_dir) / f"pype-v{version}.zip"
|
||||
self._log.info(f"creating zip: {temp_zip}")
|
||||
self._print(f"creating zip: {temp_zip}")
|
||||
|
||||
self._create_pype_zip(temp_zip, repo_dir)
|
||||
if not os.path.exists(temp_zip):
|
||||
self._log.error("make archive failed.")
|
||||
self._print("make archive failed.", LOG_ERROR)
|
||||
return None
|
||||
|
||||
destination = self.data_dir / temp_zip.name
|
||||
destination = self._move_zip_to_data_dir(temp_zip)
|
||||
|
||||
if destination.exists():
|
||||
self._log.warning(
|
||||
f"Destination file {destination} exists, removing.")
|
||||
try:
|
||||
destination.unlink()
|
||||
except Exception as e:
|
||||
self._log.error(e)
|
||||
return None
|
||||
return PypeVersion(version=version, path=destination)
|
||||
|
||||
def _move_zip_to_data_dir(self, zip_file) -> Union[None, Path]:
|
||||
"""Move zip with Pype version to user data directory.
|
||||
|
||||
Args:
|
||||
zip_file (Path): Path to zip file.
|
||||
|
||||
Returns:
|
||||
None if move fails.
|
||||
Path to moved zip on success.
|
||||
|
||||
"""
|
||||
destination = self.data_dir / zip_file.name
|
||||
|
||||
if destination.exists():
|
||||
self._print(
|
||||
f"Destination file {destination} exists, removing.",
|
||||
LOG_WARNING)
|
||||
try:
|
||||
shutil.move(temp_zip.as_posix(), self.data_dir.as_posix())
|
||||
except shutil.Error as e:
|
||||
self._log.error(e)
|
||||
destination.unlink()
|
||||
except Exception as e:
|
||||
self._print(str(e), LOG_ERROR, exc_info=True)
|
||||
return None
|
||||
try:
|
||||
shutil.move(zip_file.as_posix(), self.data_dir.as_posix())
|
||||
except shutil.Error as e:
|
||||
self._print(str(e), LOG_ERROR, exc_info=True)
|
||||
return None
|
||||
|
||||
return destination
|
||||
|
||||
def _filter_dir(self, path: Path, path_filter: List) -> List[Path]:
|
||||
"""Recursively crawl over path and filter."""
|
||||
result = []
|
||||
for item in path.iterdir():
|
||||
if item.name in path_filter:
|
||||
continue
|
||||
if item.name.startswith('.'):
|
||||
continue
|
||||
if item.is_dir():
|
||||
result.extend(self._filter_dir(item, path_filter))
|
||||
else:
|
||||
result.append(item)
|
||||
return result
|
||||
|
||||
def create_version_from_frozen_code(self) -> Union[None, PypeVersion]:
|
||||
"""Create Pype version from *frozen* code distributed by installer.
|
||||
|
||||
This should be real edge case for those wanting to try out Pype
|
||||
without setting up whole infrastructure but is strongly discouraged
|
||||
in studio setup as this use local version independent of others
|
||||
that can be out of date.
|
||||
|
||||
Returns:
|
||||
:class:`PypeVersion` zip file to be installed.
|
||||
|
||||
"""
|
||||
frozen_root = Path(sys.executable).parent
|
||||
repo_dir = frozen_root / "repos"
|
||||
repo_list = self._filter_dir(
|
||||
repo_dir, self.zip_filter)
|
||||
|
||||
# from frozen code we need igniter, pype, schema vendor
|
||||
pype_list = self._filter_dir(
|
||||
frozen_root / "pype", self.zip_filter)
|
||||
pype_list += self._filter_dir(
|
||||
frozen_root / "igniter", self.zip_filter)
|
||||
pype_list += self._filter_dir(
|
||||
frozen_root / "schema", self.zip_filter)
|
||||
pype_list += self._filter_dir(
|
||||
frozen_root / "vendor", self.zip_filter)
|
||||
pype_list.append(frozen_root / "README.md")
|
||||
pype_list.append(frozen_root / "LICENSE")
|
||||
|
||||
version = self.get_version(frozen_root)
|
||||
|
||||
# create zip inside temporary directory.
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
temp_zip = \
|
||||
Path(temp_dir) / f"pype-v{version}.zip"
|
||||
self._print(f"creating zip: {temp_zip}")
|
||||
|
||||
with ZipFile(temp_zip, "w") as zip_file:
|
||||
progress = 0
|
||||
repo_inc = 48.0 / float(len(repo_list))
|
||||
file: Path
|
||||
for file in repo_list:
|
||||
progress += repo_inc
|
||||
self._progress_callback(int(progress))
|
||||
|
||||
# archive name is relative to repos dir
|
||||
arc_name = file.relative_to(repo_dir)
|
||||
zip_file.write(file, arc_name)
|
||||
|
||||
pype_inc = 48.0 / float(len(pype_list))
|
||||
file: Path
|
||||
for file in pype_list:
|
||||
progress += pype_inc
|
||||
self._progress_callback(int(progress))
|
||||
|
||||
arc_name = file.relative_to(frozen_root.parent)
|
||||
# we need to replace first part of path which starts with
|
||||
# something like `exe.win/linux....` with `pype` as this
|
||||
# is expected by Pype in zip archive.
|
||||
arc_name = Path("pype").joinpath(*arc_name.parts[1:])
|
||||
zip_file.write(file, arc_name)
|
||||
|
||||
destination = self._move_zip_to_data_dir(temp_zip)
|
||||
|
||||
return PypeVersion(version=version, path=destination)
|
||||
|
||||
def _create_pype_zip(
|
||||
self,
|
||||
zip_path: Path, include_dir: Path,
|
||||
|
|
@ -379,23 +489,9 @@ class BootstrapRepos:
|
|||
"""
|
||||
include_dir = include_dir.resolve()
|
||||
|
||||
def _filter_dir(path: Path, path_filter: List) -> List[Path]:
|
||||
"""Recursively crawl over path and filter."""
|
||||
result = []
|
||||
for item in path.iterdir():
|
||||
if item.name in path_filter:
|
||||
continue
|
||||
if item.name.startswith('.'):
|
||||
continue
|
||||
if item.is_dir():
|
||||
result.extend(_filter_dir(item, path_filter))
|
||||
else:
|
||||
result.append(item)
|
||||
return result
|
||||
|
||||
pype_list = []
|
||||
# get filtered list of files in repositories (repos directory)
|
||||
repo_list = _filter_dir(include_dir, self.zip_filter)
|
||||
repo_list = self._filter_dir(include_dir, self.zip_filter)
|
||||
# count them
|
||||
repo_files = len(repo_list)
|
||||
|
||||
|
|
@ -404,15 +500,15 @@ class BootstrapRepos:
|
|||
pype_inc = 0
|
||||
if include_pype:
|
||||
# get filtered list of file in Pype repository
|
||||
pype_list = _filter_dir(include_dir.parent, self.zip_filter)
|
||||
pype_list = self._filter_dir(include_dir.parent, self.zip_filter)
|
||||
pype_files = len(pype_list)
|
||||
repo_inc = 48.0 / float(repo_files)
|
||||
pype_inc = 48.0 / float(pype_files)
|
||||
else:
|
||||
repo_inc = 98.0 / float(repo_files)
|
||||
progress = 0
|
||||
|
||||
with ZipFile(zip_path, "w") as zip_file:
|
||||
progress = 0
|
||||
file: Path
|
||||
for file in repo_list:
|
||||
progress += repo_inc
|
||||
|
|
@ -446,8 +542,7 @@ class BootstrapRepos:
|
|||
continue
|
||||
|
||||
processed_path = file
|
||||
self._log.debug(f"processing {processed_path}")
|
||||
self._print(f"- processing {processed_path}", False)
|
||||
self._print(f"- processing {processed_path}")
|
||||
|
||||
zip_file.write(file,
|
||||
"pype" / file.relative_to(pype_root))
|
||||
|
|
@ -468,6 +563,9 @@ class BootstrapRepos:
|
|||
Args:
|
||||
archive (Path): path to archive.
|
||||
|
||||
.. deprecated:: 3.0
|
||||
we don't use zip archives directly
|
||||
|
||||
"""
|
||||
if not archive.is_file() and not archive.exists():
|
||||
raise ValueError("Archive is not file.")
|
||||
|
|
@ -520,7 +618,7 @@ class BootstrapRepos:
|
|||
|
||||
def find_pype(
|
||||
self,
|
||||
pype_path: Path = None,
|
||||
pype_path: Union[Path, str] = None,
|
||||
staging: bool = False,
|
||||
include_zips: bool = False) -> Union[List[PypeVersion], None]:
|
||||
"""Get ordered dict of detected Pype version.
|
||||
|
|
@ -532,7 +630,8 @@ class BootstrapRepos:
|
|||
3) We use user data directory
|
||||
|
||||
Args:
|
||||
pype_path (Path, optional): Try to find Pype on the given path.
|
||||
pype_path (Path or str, optional): Try to find Pype on the given
|
||||
path or url.
|
||||
staging (bool, optional): Filter only staging version, skip them
|
||||
otherwise.
|
||||
include_zips (bool, optional): If set True it will try to find
|
||||
|
|
@ -544,7 +643,17 @@ class BootstrapRepos:
|
|||
|
||||
None: if Pype is not found.
|
||||
|
||||
Todo:
|
||||
implement git/url support as Pype location, so it would be
|
||||
possible to enter git url, Pype would check it out and if it is
|
||||
ok install it as normal version.
|
||||
|
||||
"""
|
||||
if pype_path and not isinstance(pype_path, Path):
|
||||
raise NotImplementedError(
|
||||
("Finding Pype in non-filesystem locations is"
|
||||
" not implemented yet."))
|
||||
|
||||
dir_to_search = self.data_dir
|
||||
|
||||
# if we have pype_path specified, search only there.
|
||||
|
|
@ -565,116 +674,15 @@ class BootstrapRepos:
|
|||
# nothing found in registry, we'll use data dir
|
||||
pass
|
||||
|
||||
# pype installation dir doesn't exists
|
||||
if not dir_to_search.exists():
|
||||
return None
|
||||
pype_versions = self.get_pype_versions(dir_to_search, staging)
|
||||
|
||||
_pype_versions = []
|
||||
# iterate over directory in first level and find all that might
|
||||
# contain Pype.
|
||||
for file in dir_to_search.iterdir():
|
||||
# remove zip file version if needed.
|
||||
if not include_zips:
|
||||
pype_versions = [
|
||||
v for v in pype_versions if v.path.suffix != ".zip"
|
||||
]
|
||||
|
||||
# if file, strip extension, in case of dir not.
|
||||
name = file.name if file.is_dir() else file.stem
|
||||
result = PypeVersion.version_in_str(name)
|
||||
|
||||
if result[0]:
|
||||
detected_version: PypeVersion
|
||||
detected_version = result[1]
|
||||
|
||||
if file.is_dir():
|
||||
# if item is directory that might (based on it's name)
|
||||
# contain Pype version, check if it really does contain
|
||||
# Pype and that their versions matches.
|
||||
try:
|
||||
# add one 'pype' level as inside dir there should
|
||||
# be many other repositories.
|
||||
version_str = BootstrapRepos.get_version(
|
||||
file / "pype")
|
||||
version_check = PypeVersion(version=version_str)
|
||||
except ValueError:
|
||||
self._log.error(
|
||||
f"cannot determine version from {file}")
|
||||
continue
|
||||
|
||||
version_main = version_check.get_main_version()
|
||||
detected_main = detected_version.get_main_version()
|
||||
if version_main != detected_main:
|
||||
self._log.error(
|
||||
(f"dir version ({detected_version}) and "
|
||||
f"its content version ({version_check}) "
|
||||
"doesn't match. Skipping."))
|
||||
continue
|
||||
|
||||
if file.is_file():
|
||||
|
||||
if not include_zips:
|
||||
continue
|
||||
|
||||
# skip non-zip files
|
||||
if file.suffix.lower() != ".zip":
|
||||
continue
|
||||
|
||||
# open zip file, look inside and parse version from Pype
|
||||
# inside it. If there is none, or it is different from
|
||||
# version specified in file name, skip it.
|
||||
try:
|
||||
with ZipFile(file, "r") as zip_file:
|
||||
with zip_file.open(
|
||||
"pype/pype/version.py") as version_file:
|
||||
zip_version = {}
|
||||
exec(version_file.read(), zip_version)
|
||||
version_check = PypeVersion(
|
||||
version=zip_version["__version__"])
|
||||
|
||||
version_main = version_check.get_main_version() # noqa: E501
|
||||
detected_main = detected_version.get_main_version() # noqa: E501
|
||||
|
||||
if version_main != detected_main:
|
||||
self._log.error(
|
||||
(f"zip version ({detected_version}) "
|
||||
f"and its content version "
|
||||
f"({version_check}) "
|
||||
"doesn't match. Skipping."))
|
||||
continue
|
||||
except BadZipFile:
|
||||
self._log.error(f"{file} is not zip file")
|
||||
continue
|
||||
except KeyError:
|
||||
self._log.error("Zip not containing Pype")
|
||||
continue
|
||||
|
||||
detected_version.path = file
|
||||
if staging and detected_version.is_staging():
|
||||
_pype_versions.append(detected_version)
|
||||
|
||||
if not staging and not detected_version.is_staging():
|
||||
_pype_versions.append(detected_version)
|
||||
|
||||
return sorted(_pype_versions)
|
||||
|
||||
@staticmethod
|
||||
def _get_pype_from_mongo(mongo_url: str) -> Union[Path, None]:
|
||||
"""Get path from Mongo database.
|
||||
|
||||
This sets environment variable ``PYPE_MONGO`` for
|
||||
:mod:`pype.settings` to be able to read data from database.
|
||||
It will then retrieve environment variables and among them
|
||||
must be ``PYPE_PATH``.
|
||||
|
||||
Args:
|
||||
mongo_url (str): mongodb connection url
|
||||
|
||||
Returns:
|
||||
Path: if path from ``PYPE_PATH`` is found.
|
||||
None: if not.
|
||||
|
||||
"""
|
||||
os.environ["PYPE_MONGO"] = mongo_url
|
||||
env = load_environments()
|
||||
if not env.get("PYPE_PATH"):
|
||||
return None
|
||||
return Path(env.get("PYPE_PATH"))
|
||||
return pype_versions
|
||||
|
||||
def process_entered_location(self, location: str) -> Union[Path, None]:
|
||||
"""Process user entered location string.
|
||||
|
|
@ -683,7 +691,7 @@ class BootstrapRepos:
|
|||
If it is mongodb url, it will connect and load ``PYPE_PATH`` from
|
||||
there and use it as path to Pype. In it is _not_ mongodb url, it
|
||||
is assumed we have a path, this is tested and zip file is
|
||||
produced and installed using :meth:`install_live_repos`.
|
||||
produced and installed using :meth:`create_version_from_live_code`.
|
||||
|
||||
Args:
|
||||
location (str): User entered location.
|
||||
|
|
@ -696,9 +704,9 @@ class BootstrapRepos:
|
|||
pype_path = None
|
||||
# try to get pype path from mongo.
|
||||
if location.startswith("mongodb"):
|
||||
pype_path = self._get_pype_from_mongo(location)
|
||||
pype_path = get_pype_path_from_db(location)
|
||||
if not pype_path:
|
||||
self._log.error("cannot find PYPE_PATH in settings.")
|
||||
self._print("cannot find PYPE_PATH in settings.")
|
||||
return None
|
||||
|
||||
# if not successful, consider location to be fs path.
|
||||
|
|
@ -707,108 +715,59 @@ class BootstrapRepos:
|
|||
|
||||
# test if this path does exist.
|
||||
if not pype_path.exists():
|
||||
self._log.error(f"{pype_path} doesn't exists.")
|
||||
self._print(f"{pype_path} doesn't exists.")
|
||||
return None
|
||||
|
||||
# test if entered path isn't user data dir
|
||||
if self.data_dir == pype_path:
|
||||
self._log.error("cannot point to user data dir")
|
||||
self._print("cannot point to user data dir", LOG_ERROR)
|
||||
return None
|
||||
|
||||
# find pype zip files in location. There can be
|
||||
# either "live" Pype repository, or multiple zip files or even
|
||||
# multiple pype version directories. This process looks into zip
|
||||
# files and directories and tries to parse `version.py` file.
|
||||
versions = self.find_pype(pype_path)
|
||||
versions = self.find_pype(pype_path, include_zips=True)
|
||||
if versions:
|
||||
self._log.info(f"found Pype in [ {pype_path} ]")
|
||||
self._log.info(f"latest version found is [ {versions[-1]} ]")
|
||||
self._print(f"found Pype in [ {pype_path} ]")
|
||||
self._print(f"latest version found is [ {versions[-1]} ]")
|
||||
|
||||
destination = self.data_dir / versions[-1].path.name
|
||||
|
||||
# test if destination file already exist, if so lets delete it.
|
||||
# we consider path on location as authoritative place.
|
||||
if destination.exists():
|
||||
try:
|
||||
destination.unlink()
|
||||
except OSError:
|
||||
self._log.error(
|
||||
f"cannot remove already existing {destination}",
|
||||
exc_info=True)
|
||||
return None
|
||||
|
||||
# create destination parent directories even if they don't exist.
|
||||
if not destination.parent.exists():
|
||||
destination.parent.mkdir(parents=True)
|
||||
|
||||
# latest version found is directory
|
||||
if versions[-1].path.is_dir():
|
||||
# zip it, copy it and extract it
|
||||
# create zip inside temporary directory.
|
||||
self._log.info("Creating zip from directory ...")
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
temp_zip = \
|
||||
Path(temp_dir) / f"pype-v{versions[-1]}.zip"
|
||||
self._log.info(f"creating zip: {temp_zip}")
|
||||
|
||||
self._create_pype_zip(temp_zip, versions[-1].path)
|
||||
if not os.path.exists(temp_zip):
|
||||
self._log.error("make archive failed.")
|
||||
return None
|
||||
|
||||
destination = self.data_dir / temp_zip.name
|
||||
|
||||
elif versions[-1].path.is_file():
|
||||
# in this place, it must be zip file as `find_pype()` is
|
||||
# checking just that.
|
||||
assert versions[-1].path.suffix.lower() == ".zip", (
|
||||
"Invalid file format"
|
||||
)
|
||||
try:
|
||||
self._log.info("Copying zip to destination ...")
|
||||
copyfile(versions[-1].path.as_posix(), destination.as_posix())
|
||||
except OSError:
|
||||
self._log.error(
|
||||
"cannot copy detected version to user data directory",
|
||||
exc_info=True)
|
||||
return None
|
||||
|
||||
# extract zip there
|
||||
self._log.info("extracting zip to destination ...")
|
||||
with ZipFile(versions[-1].path, "r") as zip_ref:
|
||||
zip_ref.extractall(destination)
|
||||
|
||||
return destination
|
||||
return self.install_version(versions[-1])
|
||||
|
||||
# if we got here, it means that location is "live" Pype repository.
|
||||
# we'll create zip from it and move it to user data dir.
|
||||
repo_file = self.install_live_repos(pype_path)
|
||||
if not repo_file.exists():
|
||||
self._log.error(f"installing zip {repo_file} failed.")
|
||||
live_pype = self.create_version_from_live_code(pype_path)
|
||||
if not live_pype.path.exists():
|
||||
self._print(f"installing zip {live_pype} failed.", LOG_ERROR)
|
||||
return None
|
||||
# install it
|
||||
return self.install_version(live_pype)
|
||||
|
||||
destination = self.data_dir / repo_file.stem
|
||||
if destination.exists():
|
||||
try:
|
||||
destination.unlink()
|
||||
except OSError:
|
||||
self._log.error(
|
||||
f"cannot remove already existing {destination}",
|
||||
exc_info=True)
|
||||
return None
|
||||
def _print(self,
|
||||
message: str,
|
||||
level: int = LOG_INFO,
|
||||
exc_info: bool = False):
|
||||
"""Helper function passing logs to UI and to logger.
|
||||
|
||||
destination.mkdir(parents=True)
|
||||
Supporting 3 levels of logs defined with `LOG_INFO`, `LOG_WARNING` and
|
||||
`LOG_ERROR` constants.
|
||||
|
||||
# extract zip there
|
||||
self._log.info("extracting zip to destination ...")
|
||||
with ZipFile(versions[-1].path, "r") as zip_ref:
|
||||
zip_ref.extractall(destination)
|
||||
Args:
|
||||
message (str): Message to log.
|
||||
level (int, optional): Log level to use.
|
||||
exc_info (bool, optional): Exception info object to pass to logger.
|
||||
|
||||
return destination
|
||||
|
||||
def _print(self, message, error=False):
|
||||
"""
|
||||
if self._message:
|
||||
self._message.emit(message, error)
|
||||
self._message.emit(message, level == LOG_ERROR)
|
||||
|
||||
if level == LOG_WARNING:
|
||||
self._log.warning(message, exc_info=exc_info)
|
||||
return
|
||||
if level == LOG_ERROR:
|
||||
self._log.error(message, exc_info=exc_info)
|
||||
return
|
||||
self._log.info(message, exc_info=exc_info)
|
||||
|
||||
def extract_pype(self, version: PypeVersion) -> Union[Path, None]:
|
||||
"""Extract zipped Pype version to user data directory.
|
||||
|
|
@ -829,12 +788,9 @@ class BootstrapRepos:
|
|||
if destination.exists():
|
||||
try:
|
||||
destination.unlink()
|
||||
except OSError as e:
|
||||
except OSError:
|
||||
msg = f"!!! Cannot remove already existing {destination}"
|
||||
self._log.error(msg)
|
||||
self._log.error(e.strerror)
|
||||
self._print(msg, True)
|
||||
self._print(e.strerror, True)
|
||||
self._print(msg, LOG_ERROR, exc_info=True)
|
||||
return None
|
||||
|
||||
destination.mkdir(parents=True)
|
||||
|
|
@ -848,7 +804,29 @@ class BootstrapRepos:
|
|||
|
||||
return destination
|
||||
|
||||
def install_version(self, pype_version: PypeVersion, force: bool = False):
|
||||
def is_inside_user_data(self, path: Path) -> bool:
|
||||
"""Test if version is located in user data dir.
|
||||
|
||||
Args:
|
||||
path (Path) Path to test.
|
||||
|
||||
Returns:
|
||||
True if path is inside user data dir.
|
||||
|
||||
"""
|
||||
is_inside = False
|
||||
try:
|
||||
is_inside = path.resolve().relative_to(
|
||||
self.data_dir)
|
||||
except ValueError:
|
||||
# if relative path cannot be calculated, Pype version is not
|
||||
# inside user data dir
|
||||
pass
|
||||
return is_inside
|
||||
|
||||
def install_version(self,
|
||||
pype_version: PypeVersion,
|
||||
force: bool = False) -> Path:
|
||||
"""Install Pype version to user data directory.
|
||||
|
||||
Args:
|
||||
|
|
@ -866,52 +844,46 @@ class BootstrapRepos:
|
|||
|
||||
"""
|
||||
|
||||
# test if version is located (in user data dir)
|
||||
is_inside = False
|
||||
try:
|
||||
is_inside = pype_version.path.resolve().relative_to(
|
||||
self.data_dir)
|
||||
except ValueError:
|
||||
# if relative path cannot be calculated, Pype version is not
|
||||
# inside user data dir
|
||||
pass
|
||||
|
||||
if is_inside:
|
||||
if self.is_inside_user_data(pype_version.path) and not pype_version.path.is_file(): # noqa
|
||||
raise PypeVersionExists("Pype already inside user data dir")
|
||||
|
||||
# determine destination directory name
|
||||
# for zip file strip suffix
|
||||
destination = self.data_dir / pype_version.path.stem
|
||||
# for zip file strip suffix, in case of dir use whole dir name
|
||||
if pype_version.path.is_dir():
|
||||
dir_name = pype_version.path.name
|
||||
else:
|
||||
dir_name = pype_version.path.stem
|
||||
|
||||
# test if destination file already exist, if so lets delete it.
|
||||
# we consider path on location as authoritative place.
|
||||
destination = self.data_dir / dir_name
|
||||
|
||||
# test if destination directory already exist, if so lets delete it.
|
||||
if destination.exists() and force:
|
||||
try:
|
||||
destination.unlink()
|
||||
except OSError:
|
||||
self._log.error(
|
||||
shutil.rmtree(destination)
|
||||
except OSError as e:
|
||||
self._print(
|
||||
f"cannot remove already existing {destination}",
|
||||
exc_info=True)
|
||||
return None
|
||||
else:
|
||||
LOG_ERROR, exc_info=True)
|
||||
raise PypeVersionIOError(
|
||||
f"cannot remove existing {destination}") from e
|
||||
elif destination.exists() and not force:
|
||||
raise PypeVersionExists(f"{destination} already exist.")
|
||||
|
||||
# create destination parent directories even if they don't exist.
|
||||
if not destination.exists():
|
||||
else:
|
||||
# create destination parent directories even if they don't exist.
|
||||
destination.mkdir(parents=True)
|
||||
|
||||
# version is directory
|
||||
if pype_version.path.is_dir():
|
||||
# create zip inside temporary directory.
|
||||
self._log.info("Creating zip from directory ...")
|
||||
self._print("Creating zip from directory ...")
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
temp_zip = \
|
||||
Path(temp_dir) / f"pype-v{pype_version}.zip"
|
||||
self._log.info(f"creating zip: {temp_zip}")
|
||||
self._print(f"creating zip: {temp_zip}")
|
||||
|
||||
self._create_pype_zip(temp_zip, pype_version.path)
|
||||
if not os.path.exists(temp_zip):
|
||||
self._log.error("make archive failed.")
|
||||
self._print("make archive failed.", LOG_ERROR)
|
||||
raise PypeVersionIOError("Zip creation failed.")
|
||||
|
||||
# set zip as version source
|
||||
|
|
@ -922,24 +894,164 @@ class BootstrapRepos:
|
|||
if pype_version.path.suffix.lower() != ".zip":
|
||||
raise PypeVersionInvalid("Invalid file format")
|
||||
|
||||
try:
|
||||
# copy file to destination
|
||||
self._log.info("Copying zip to destination ...")
|
||||
copyfile(pype_version.path.as_posix(), destination.as_posix())
|
||||
except OSError as e:
|
||||
self._log.error(
|
||||
"cannot copy version to user data directory",
|
||||
exc_info=True)
|
||||
raise PypeVersionIOError(
|
||||
"can't copy version to destination") from e
|
||||
if not self.is_inside_user_data(pype_version.path):
|
||||
try:
|
||||
# copy file to destination
|
||||
self._print("Copying zip to destination ...")
|
||||
_destination_zip = destination.parent / pype_version.path.name
|
||||
copyfile(
|
||||
pype_version.path.as_posix(),
|
||||
_destination_zip.as_posix())
|
||||
except OSError as e:
|
||||
self._print(
|
||||
"cannot copy version to user data directory", LOG_ERROR,
|
||||
exc_info=True)
|
||||
raise PypeVersionIOError((
|
||||
f"can't copy version {pype_version.path.as_posix()} "
|
||||
f"to destination {destination.parent.as_posix()}")) from e
|
||||
|
||||
# extract zip there
|
||||
self._log.info("extracting zip to destination ...")
|
||||
self._print("extracting zip to destination ...")
|
||||
with ZipFile(pype_version.path, "r") as zip_ref:
|
||||
zip_ref.extractall(destination)
|
||||
|
||||
return destination
|
||||
|
||||
def _is_pype_in_dir(self,
|
||||
dir_item: Path,
|
||||
detected_version: PypeVersion) -> bool:
|
||||
"""Test if path item is Pype version matching detected version.
|
||||
|
||||
If item is directory that might (based on it's name)
|
||||
contain Pype version, check if it really does contain
|
||||
Pype and that their versions matches.
|
||||
|
||||
Args:
|
||||
dir_item (Path): Directory to test.
|
||||
detected_version (PypeVersion): Pype version detected from name.
|
||||
|
||||
Returns:
|
||||
True if it is valid Pype version, False otherwise.
|
||||
|
||||
"""
|
||||
try:
|
||||
# add one 'pype' level as inside dir there should
|
||||
# be many other repositories.
|
||||
version_str = BootstrapRepos.get_version(
|
||||
dir_item / "pype")
|
||||
version_check = PypeVersion(version=version_str)
|
||||
except ValueError:
|
||||
self._print(
|
||||
f"cannot determine version from {dir_item}", True)
|
||||
return False
|
||||
|
||||
version_main = version_check.get_main_version()
|
||||
detected_main = detected_version.get_main_version()
|
||||
if version_main != detected_main:
|
||||
self._print(
|
||||
(f"dir version ({detected_version}) and "
|
||||
f"its content version ({version_check}) "
|
||||
"doesn't match. Skipping."))
|
||||
return False
|
||||
return True
|
||||
|
||||
def _is_pype_in_zip(self,
|
||||
zip_item: Path,
|
||||
detected_version: PypeVersion) -> bool:
|
||||
"""Test if zip path is Pype version matching detected version.
|
||||
|
||||
Open zip file, look inside and parse version from Pype
|
||||
inside it. If there is none, or it is different from
|
||||
version specified in file name, skip it.
|
||||
|
||||
Args:
|
||||
zip_item (Path): Zip file to test.
|
||||
detected_version (PypeVersion): Pype version detected from name.
|
||||
|
||||
Returns:
|
||||
True if it is valid Pype version, False otherwise.
|
||||
|
||||
"""
|
||||
# skip non-zip files
|
||||
if zip_item.suffix.lower() != ".zip":
|
||||
return False
|
||||
|
||||
try:
|
||||
with ZipFile(zip_item, "r") as zip_file:
|
||||
with zip_file.open(
|
||||
"pype/pype/version.py") as version_file:
|
||||
zip_version = {}
|
||||
exec(version_file.read(), zip_version)
|
||||
version_check = PypeVersion(
|
||||
version=zip_version["__version__"])
|
||||
|
||||
version_main = version_check.get_main_version() # noqa: E501
|
||||
detected_main = detected_version.get_main_version() # noqa: E501
|
||||
|
||||
if version_main != detected_main:
|
||||
self._print(
|
||||
(f"zip version ({detected_version}) "
|
||||
f"and its content version "
|
||||
f"({version_check}) "
|
||||
"doesn't match. Skipping."), True)
|
||||
return False
|
||||
except BadZipFile:
|
||||
self._print(f"{zip_item} is not a zip file", True)
|
||||
return False
|
||||
except KeyError:
|
||||
self._print("Zip does not contain Pype", True)
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_pype_versions(self, pype_dir: Path, staging: bool = False) -> list:
|
||||
"""Get all detected Pype versions in directory.
|
||||
|
||||
Args:
|
||||
pype_dir (Path): Directory to scan.
|
||||
staging (bool, optional): Find staging versions if True.
|
||||
|
||||
Returns:
|
||||
list of PypeVersion
|
||||
|
||||
Throws:
|
||||
ValueError: if invalid path is specified.
|
||||
|
||||
"""
|
||||
if not pype_dir.exists() and not pype_dir.is_dir():
|
||||
raise ValueError("specified directory is invalid")
|
||||
|
||||
_pype_versions = []
|
||||
# iterate over directory in first level and find all that might
|
||||
# contain Pype.
|
||||
for item in pype_dir.iterdir():
|
||||
|
||||
# if file, strip extension, in case of dir not.
|
||||
name = item.name if item.is_dir() else item.stem
|
||||
result = PypeVersion.version_in_str(name)
|
||||
|
||||
if result[0]:
|
||||
detected_version: PypeVersion
|
||||
detected_version = result[1]
|
||||
|
||||
if item.is_dir() and not self._is_pype_in_dir(
|
||||
item, detected_version
|
||||
):
|
||||
continue
|
||||
|
||||
if item.is_file() and not self._is_pype_in_zip(
|
||||
item, detected_version
|
||||
):
|
||||
continue
|
||||
|
||||
detected_version.path = item
|
||||
if staging and detected_version.is_staging():
|
||||
_pype_versions.append(detected_version)
|
||||
|
||||
if not staging and not detected_version.is_staging():
|
||||
_pype_versions.append(detected_version)
|
||||
|
||||
return sorted(_pype_versions)
|
||||
|
||||
|
||||
class PypeVersionExists(Exception):
|
||||
"""Exception for handling existing Pype version."""
|
||||
|
|
|
|||
|
|
@ -3,25 +3,54 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
from Qt import QtCore, QtGui, QtWidgets
|
||||
from Qt.QtGui import QValidator
|
||||
from Qt import QtCore, QtGui, QtWidgets # noqa
|
||||
from Qt.QtGui import QValidator # noqa
|
||||
from Qt.QtCore import QTimer # noqa
|
||||
|
||||
from .install_thread import InstallThread
|
||||
from .tools import validate_path_string, validate_mongo_connection
|
||||
from .install_thread import InstallThread, InstallResult
|
||||
from .tools import (
|
||||
validate_path_string,
|
||||
validate_mongo_connection,
|
||||
get_pype_path_from_db
|
||||
)
|
||||
from .user_settings import PypeSettingsRegistry
|
||||
from .version import __version__
|
||||
|
||||
|
||||
class FocusHandlingLineEdit(QtWidgets.QLineEdit):
|
||||
"""Handling focus in/out on QLineEdit."""
|
||||
focusIn = QtCore.Signal()
|
||||
focusOut = QtCore.Signal()
|
||||
|
||||
def focusOutEvent(self, event): # noqa
|
||||
"""For emitting signal on focus out."""
|
||||
self.focusOut.emit()
|
||||
super().focusOutEvent(event)
|
||||
|
||||
def focusInEvent(self, event): # noqa
|
||||
"""For emitting signal on focus in."""
|
||||
self.focusIn.emit()
|
||||
super().focusInEvent(event)
|
||||
|
||||
|
||||
class InstallDialog(QtWidgets.QDialog):
|
||||
"""Main Igniter dialog window."""
|
||||
_size_w = 400
|
||||
_size_h = 400
|
||||
_path = None
|
||||
_size_h = 600
|
||||
path = ""
|
||||
_controls_disabled = False
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(InstallDialog, self).__init__(parent)
|
||||
self.registry = PypeSettingsRegistry()
|
||||
|
||||
self._mongo_url = os.getenv("PYPE_MONGO", "")
|
||||
self.mongo_url = ""
|
||||
try:
|
||||
self.mongo_url = os.getenv("PYPE_MONGO", "") or self.registry.get_secure_item("pypeMongo") # noqa: E501
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
self.setWindowTitle("Pype - Configure Pype repository path")
|
||||
self.setWindowTitle(f"Pype Igniter {__version__} - Pype installation")
|
||||
self._icon_path = os.path.join(
|
||||
os.path.dirname(__file__), 'pype_icon.png')
|
||||
icon = QtGui.QIcon(self._icon_path)
|
||||
|
|
@ -34,7 +63,7 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
self.setMinimumSize(
|
||||
QtCore.QSize(self._size_w, self._size_h))
|
||||
self.setMaximumSize(
|
||||
QtCore.QSize(self._size_w + 100, self._size_h + 100))
|
||||
QtCore.QSize(self._size_w + 100, self._size_h + 500))
|
||||
|
||||
# style for normal console text
|
||||
self.default_console_style = QtGui.QTextCharFormat()
|
||||
|
|
@ -52,6 +81,7 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
os.path.join(
|
||||
os.path.dirname(__file__), 'RobotoMono-Regular.ttf')
|
||||
)
|
||||
self._pype_run_ready = False
|
||||
|
||||
self._init_ui()
|
||||
|
||||
|
|
@ -70,7 +100,7 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
"""Welcome to <b>Pype</b>
|
||||
<p>
|
||||
We've detected <b>Pype</b> is not configured yet. But don't worry,
|
||||
this is as easy as setting one thing.
|
||||
this is as easy as setting one or two things.
|
||||
<p>
|
||||
""")
|
||||
self.main_label.setWordWrap(True)
|
||||
|
|
@ -80,11 +110,16 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
# --------------------------------------------------------------------
|
||||
|
||||
self.pype_path_label = QtWidgets.QLabel(
|
||||
"""This can be either <b>Path to studio location</b>
|
||||
or <b>Database connection string</b> or <b>Pype Token</b>.
|
||||
"""This is <b>Path to studio location</b> where Pype versions
|
||||
are stored. It will be pre-filled if your MongoDB connection is
|
||||
already set and your studio defined this location.
|
||||
<p>
|
||||
Leave it empty if you want to use Pype version that come with this
|
||||
installation.
|
||||
Leave it empty if you want to install Pype version that comes with
|
||||
this installation.
|
||||
</p>
|
||||
<p>
|
||||
If you want to just try Pype without installing, hit the middle
|
||||
button that states "run without installation".
|
||||
</p>
|
||||
"""
|
||||
)
|
||||
|
|
@ -98,9 +133,9 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
input_layout = QtWidgets.QHBoxLayout()
|
||||
|
||||
input_layout.setContentsMargins(0, 10, 0, 10)
|
||||
self.user_input = QtWidgets.QLineEdit()
|
||||
self.user_input = FocusHandlingLineEdit()
|
||||
|
||||
self.user_input.setPlaceholderText("Pype repository path or url")
|
||||
self.user_input.setPlaceholderText("Path to Pype versions")
|
||||
self.user_input.textChanged.connect(self._path_changed)
|
||||
self.user_input.setStyleSheet(
|
||||
("color: rgb(233, 233, 233);"
|
||||
|
|
@ -129,15 +164,28 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
# Mongo box | OK button
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
self.mongo_label = QtWidgets.QLabel(
|
||||
"""Enter URL for running MongoDB instance:"""
|
||||
)
|
||||
|
||||
self.mongo_label.setWordWrap(True)
|
||||
self.mongo_label.setStyleSheet("color: rgb(150, 150, 150);")
|
||||
|
||||
class MongoWidget(QtWidgets.QWidget):
|
||||
"""Widget to input mongodb URL."""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
self._btn_mongo = None
|
||||
super(MongoWidget, self).__init__(parent)
|
||||
mongo_layout = QtWidgets.QHBoxLayout()
|
||||
mongo_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self._mongo_input = QtWidgets.QLineEdit()
|
||||
self._mongo_input = FocusHandlingLineEdit()
|
||||
self._mongo_input.setPlaceholderText("Mongo URL")
|
||||
self._mongo_input.textChanged.connect(self._mongo_changed)
|
||||
self._mongo_input.focusIn.connect(self._focus_in)
|
||||
self._mongo_input.focusOut.connect(self._focus_out)
|
||||
self._mongo_input.setValidator(
|
||||
MongoValidator(self._mongo_input))
|
||||
self._mongo_input.setStyleSheet(
|
||||
("color: rgb(233, 233, 233);"
|
||||
"background-color: rgb(64, 64, 64);"
|
||||
|
|
@ -148,16 +196,37 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
mongo_layout.addWidget(self._mongo_input)
|
||||
self.setLayout(mongo_layout)
|
||||
|
||||
def _focus_out(self):
|
||||
self.validate_url()
|
||||
|
||||
def _focus_in(self):
|
||||
self._mongo_input.setStyleSheet(
|
||||
"""
|
||||
background-color: rgb(32, 32, 19);
|
||||
color: rgb(255, 190, 15);
|
||||
padding: 0.5em;
|
||||
border: 1px solid rgb(64, 64, 32);
|
||||
"""
|
||||
)
|
||||
|
||||
def _mongo_changed(self, mongo: str):
|
||||
self.parent().mongo_url = mongo
|
||||
|
||||
def get_mongo_url(self):
|
||||
def get_mongo_url(self) -> str:
|
||||
"""Helper to get url from parent."""
|
||||
return self.parent().mongo_url
|
||||
|
||||
def set_mongo_url(self, mongo: str):
|
||||
"""Helper to set url to parent.
|
||||
|
||||
Args:
|
||||
mongo (str): mongodb url string.
|
||||
|
||||
"""
|
||||
self._mongo_input.setText(mongo)
|
||||
|
||||
def set_valid(self):
|
||||
"""Set valid state on mongo url input."""
|
||||
self._mongo_input.setStyleSheet(
|
||||
"""
|
||||
background-color: rgb(19, 19, 19);
|
||||
|
|
@ -166,20 +235,48 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
border: 1px solid rgb(32, 64, 32);
|
||||
"""
|
||||
)
|
||||
self.parent().install_button.setEnabled(True)
|
||||
|
||||
def set_invalid(self):
|
||||
"""Set invalid state on mongo url input."""
|
||||
self._mongo_input.setStyleSheet(
|
||||
"""
|
||||
background-color: rgb(32, 19, 19);
|
||||
color: rgb(255, 69, 0);
|
||||
padding: 0.5em;
|
||||
border: 1px solid rgb(32, 64, 32);
|
||||
border: 1px solid rgb(64, 32, 32);
|
||||
"""
|
||||
)
|
||||
self.parent().install_button.setEnabled(False)
|
||||
|
||||
def set_read_only(self, state: bool):
|
||||
"""Set input read-only."""
|
||||
self._mongo_input.setReadOnly(state)
|
||||
|
||||
def validate_url(self) -> bool:
|
||||
"""Validate if entered url is ok.
|
||||
|
||||
Returns:
|
||||
True if url is valid monogo string.
|
||||
|
||||
"""
|
||||
if self.parent().mongo_url == "":
|
||||
return False
|
||||
|
||||
is_valid, reason_str = validate_mongo_connection(
|
||||
self.parent().mongo_url
|
||||
)
|
||||
if not is_valid:
|
||||
self.set_invalid()
|
||||
self.parent().update_console(f"!!! {reason_str}", True)
|
||||
return False
|
||||
else:
|
||||
self.set_valid()
|
||||
return True
|
||||
|
||||
self._mongo = MongoWidget(self)
|
||||
if self._mongo_url:
|
||||
self._mongo.set_mongo_url(self._mongo_url)
|
||||
if self.mongo_url:
|
||||
self._mongo.set_mongo_url(self.mongo_url)
|
||||
|
||||
# Bottom button bar
|
||||
# --------------------------------------------------------------------
|
||||
|
|
@ -193,16 +290,29 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
pype_logo_label.setPixmap(pype_logo)
|
||||
pype_logo_label.setContentsMargins(10, 0, 0, 10)
|
||||
|
||||
self._ok_button = QtWidgets.QPushButton("OK")
|
||||
self._ok_button.setStyleSheet(
|
||||
# install button - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
self.install_button = QtWidgets.QPushButton("Install")
|
||||
self.install_button.setStyleSheet(
|
||||
("color: rgb(64, 64, 64);"
|
||||
"background-color: rgb(72, 200, 150);"
|
||||
"padding: 0.5em;")
|
||||
)
|
||||
self._ok_button.setMinimumSize(64, 24)
|
||||
self._ok_button.setToolTip("Save and continue")
|
||||
self._ok_button.clicked.connect(self._on_ok_clicked)
|
||||
self.install_button.setMinimumSize(64, 24)
|
||||
self.install_button.setToolTip("Install Pype")
|
||||
self.install_button.clicked.connect(self._on_ok_clicked)
|
||||
|
||||
# run from current button - - - - - - - - - - - - - - - - - - - - - -
|
||||
self.run_button = QtWidgets.QPushButton("Run without installation")
|
||||
self.run_button.setStyleSheet(
|
||||
("color: rgb(64, 64, 64);"
|
||||
"background-color: rgb(200, 164, 64);"
|
||||
"padding: 0.5em;")
|
||||
)
|
||||
self.run_button.setMinimumSize(64, 24)
|
||||
self.run_button.setToolTip("Run without installing Pype")
|
||||
self.run_button.clicked.connect(self._on_run_clicked)
|
||||
|
||||
# install button - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
self._exit_button = QtWidgets.QPushButton("Exit")
|
||||
self._exit_button.setStyleSheet(
|
||||
("color: rgb(64, 64, 64);"
|
||||
|
|
@ -210,14 +320,16 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
"padding: 0.5em;")
|
||||
)
|
||||
self._exit_button.setMinimumSize(64, 24)
|
||||
self._exit_button.setToolTip("Exit without saving")
|
||||
self._exit_button.setToolTip("Exit")
|
||||
self._exit_button.clicked.connect(self._on_exit_clicked)
|
||||
|
||||
bottom_layout.setContentsMargins(0, 10, 0, 0)
|
||||
bottom_layout.addWidget(pype_logo_label)
|
||||
bottom_layout.setContentsMargins(0, 10, 10, 0)
|
||||
bottom_layout.setAlignment(QtCore.Qt.AlignVCenter)
|
||||
bottom_layout.addWidget(pype_logo_label, 0, QtCore.Qt.AlignVCenter)
|
||||
bottom_layout.addStretch(1)
|
||||
bottom_layout.addWidget(self._ok_button)
|
||||
bottom_layout.addWidget(self._exit_button)
|
||||
bottom_layout.addWidget(self.install_button, 0, QtCore.Qt.AlignVCenter)
|
||||
bottom_layout.addWidget(self.run_button, 0, QtCore.Qt.AlignVCenter)
|
||||
bottom_layout.addWidget(self._exit_button, 0, QtCore.Qt.AlignVCenter)
|
||||
|
||||
bottom_widget.setLayout(bottom_layout)
|
||||
bottom_widget.setStyleSheet("background-color: rgb(32, 32, 32);")
|
||||
|
|
@ -239,6 +351,7 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
color: rgb(72, 200, 150);
|
||||
font-family: "Roboto Mono";
|
||||
font-size: 0.5em;
|
||||
border: 1px solid rgb(48, 48, 48);
|
||||
}
|
||||
QScrollBar:vertical {
|
||||
border: 1px solid rgb(61, 115, 97);
|
||||
|
|
@ -291,17 +404,25 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
"""
|
||||
)
|
||||
# add all to main
|
||||
main.addWidget(self.main_label)
|
||||
main.addWidget(self.pype_path_label)
|
||||
main.addLayout(input_layout)
|
||||
main.addWidget(self._mongo)
|
||||
main.addStretch(1)
|
||||
main.addWidget(self._status_label)
|
||||
main.addWidget(self._status_box)
|
||||
main.addWidget(self._progress_bar)
|
||||
main.addWidget(bottom_widget)
|
||||
main.addWidget(self.main_label, 0)
|
||||
main.addWidget(self.pype_path_label, 0)
|
||||
main.addLayout(input_layout, 0)
|
||||
main.addWidget(self.mongo_label, 0)
|
||||
main.addWidget(self._mongo, 0)
|
||||
|
||||
main.addWidget(self._status_label, 0)
|
||||
main.addWidget(self._status_box, 1)
|
||||
|
||||
main.addWidget(self._progress_bar, 0)
|
||||
main.addWidget(bottom_widget, 0)
|
||||
|
||||
self.setLayout(main)
|
||||
|
||||
# if mongo url is ok, try to get pype path from there
|
||||
if self._mongo.validate_url() and len(self.path) == 0:
|
||||
self.path = get_pype_path_from_db(self.mongo_url)
|
||||
self.user_input.setText(self.path)
|
||||
|
||||
def _on_select_clicked(self):
|
||||
"""Show directory dialog."""
|
||||
options = QtWidgets.QFileDialog.Options()
|
||||
|
|
@ -317,78 +438,81 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
if not result:
|
||||
return
|
||||
|
||||
filename = result[0]
|
||||
filename = QtCore.QDir.toNativeSeparators(filename)
|
||||
filename = QtCore.QDir.toNativeSeparators(result)
|
||||
|
||||
if os.path.isdir(filename):
|
||||
self.path = filename
|
||||
self.user_input.setText(filename)
|
||||
|
||||
def _on_run_clicked(self):
|
||||
valid, reason = validate_mongo_connection(
|
||||
self._mongo.get_mongo_url()
|
||||
)
|
||||
if not valid:
|
||||
self._mongo.set_invalid()
|
||||
self.update_console(f"!!! {reason}", True)
|
||||
return
|
||||
else:
|
||||
self._mongo.set_valid()
|
||||
|
||||
self.done(2)
|
||||
|
||||
def _on_ok_clicked(self):
|
||||
"""Start install process.
|
||||
|
||||
This will once again validate entered path and if ok, start
|
||||
This will once again validate entered path and mongo if ok, start
|
||||
working thread that will do actual job.
|
||||
"""
|
||||
valid, reason = validate_path_string(self._path)
|
||||
valid, reason = validate_mongo_connection(
|
||||
self._mongo.get_mongo_url()
|
||||
)
|
||||
if not valid:
|
||||
self.user_input.setStyleSheet(
|
||||
"""
|
||||
background-color: rgb(32, 19, 19);
|
||||
color: rgb(255, 69, 0);
|
||||
padding: 0.5em;
|
||||
border: 1px solid rgb(32, 64, 32);
|
||||
"""
|
||||
)
|
||||
self._update_console(reason, True)
|
||||
self._mongo.set_invalid()
|
||||
self.update_console(f"!!! {reason}", True)
|
||||
return
|
||||
else:
|
||||
self.user_input.setStyleSheet(
|
||||
"""
|
||||
background-color: rgb(19, 19, 19);
|
||||
color: rgb(64, 230, 132);
|
||||
padding: 0.5em;
|
||||
border: 1px solid rgb(32, 64, 32);
|
||||
"""
|
||||
)
|
||||
if not self._path or not self._path.startswith("mongodb"):
|
||||
valid, reason = validate_mongo_connection(
|
||||
self._mongo.get_mongo_url()
|
||||
)
|
||||
if not valid:
|
||||
self._mongo.set_invalid()
|
||||
self._update_console(f"!!! {reason}", True)
|
||||
return
|
||||
else:
|
||||
self._mongo.set_valid()
|
||||
self._mongo.set_valid()
|
||||
|
||||
if self._pype_run_ready:
|
||||
self.done(3)
|
||||
return
|
||||
|
||||
if self.path and len(self.path) > 0:
|
||||
valid, reason = validate_path_string(self.path)
|
||||
|
||||
if not valid:
|
||||
self.update_console(f"!!! {reason}", True)
|
||||
return
|
||||
|
||||
self._disable_buttons()
|
||||
self._install_thread = InstallThread(self)
|
||||
self._install_thread.message.connect(self._update_console)
|
||||
self._install_thread = InstallThread(
|
||||
self.install_result_callback_handler, self)
|
||||
self._install_thread.message.connect(self.update_console)
|
||||
self._install_thread.progress.connect(self._update_progress)
|
||||
self._install_thread.finished.connect(self._enable_buttons)
|
||||
self._install_thread.set_path(self._path)
|
||||
self._install_thread.set_path(self.path)
|
||||
self._install_thread.set_mongo(self._mongo.get_mongo_url())
|
||||
self._install_thread.start()
|
||||
|
||||
def install_result_callback_handler(self, result: InstallResult):
|
||||
"""Change button behaviour based on installation outcome."""
|
||||
status = result.status
|
||||
if status >= 0:
|
||||
self.install_button.setText("Run installed Pype")
|
||||
self._pype_run_ready = True
|
||||
|
||||
def _update_progress(self, progress: int):
|
||||
self._progress_bar.setValue(progress)
|
||||
|
||||
def _on_exit_clicked(self):
|
||||
self.close()
|
||||
self.reject()
|
||||
|
||||
def _path_changed(self, path: str) -> str:
|
||||
"""Set path."""
|
||||
self._path = path
|
||||
if not self._path.startswith("mongodb"):
|
||||
self._mongo.setVisible(True)
|
||||
else:
|
||||
self._mongo.setVisible(False)
|
||||
|
||||
if len(self._path) < 1:
|
||||
self._mongo.setVisible(False)
|
||||
self.path = path
|
||||
return path
|
||||
|
||||
def _update_console(self, msg: str, error: bool = False) -> None:
|
||||
def update_console(self, msg: str, error: bool = False) -> None:
|
||||
"""Display message in console.
|
||||
|
||||
Args:
|
||||
|
|
@ -404,36 +528,46 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
def _disable_buttons(self):
|
||||
"""Disable buttons so user interaction doesn't interfere."""
|
||||
self._btn_select.setEnabled(False)
|
||||
self.run_button.setEnabled(False)
|
||||
self._exit_button.setEnabled(False)
|
||||
self._ok_button.setEnabled(False)
|
||||
self.install_button.setEnabled(False)
|
||||
self._controls_disabled = True
|
||||
|
||||
def _enable_buttons(self):
|
||||
"""Enable buttons after operation is complete."""
|
||||
self._btn_select.setEnabled(True)
|
||||
self.run_button.setEnabled(True)
|
||||
self._exit_button.setEnabled(True)
|
||||
self._ok_button.setEnabled(True)
|
||||
self.install_button.setEnabled(True)
|
||||
self._controls_disabled = False
|
||||
|
||||
def closeEvent(self, event):
|
||||
def closeEvent(self, event): # noqa
|
||||
"""Prevent closing if window when controls are disabled."""
|
||||
if self._controls_disabled:
|
||||
return event.ignore()
|
||||
return super(InstallDialog, self).closeEvent(event)
|
||||
|
||||
|
||||
class PathValidator(QValidator):
|
||||
class MongoValidator(QValidator):
|
||||
"""Validate mongodb url for Qt widgets."""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
def __init__(self, parent=None, intermediate=False):
|
||||
self.parent = parent
|
||||
super(PathValidator, self).__init__(parent)
|
||||
self.intermediate = intermediate
|
||||
self._validate_lock = False
|
||||
self.timer = QTimer()
|
||||
self.timer.timeout.connect(self._unlock_validator)
|
||||
super().__init__(parent)
|
||||
|
||||
def _unlock_validator(self):
|
||||
self._validate_lock = False
|
||||
|
||||
def _return_state(
|
||||
self, state: QValidator.State, reason: str, path: str, pos: int):
|
||||
self, state: QValidator.State, reason: str, mongo: str):
|
||||
"""Set stylesheets and actions on parent based on state.
|
||||
|
||||
Warning:
|
||||
This will always return `QFileDialog.State.Acceptable` as
|
||||
This will always return `QValidator.State.Acceptable` as
|
||||
anything different will stop input to `QLineEdit`
|
||||
|
||||
"""
|
||||
|
|
@ -445,10 +579,10 @@ class PathValidator(QValidator):
|
|||
background-color: rgb(32, 19, 19);
|
||||
color: rgb(255, 69, 0);
|
||||
padding: 0.5em;
|
||||
border: 1px solid rgb(32, 64, 32);
|
||||
border: 1px solid rgb(64, 32, 32);
|
||||
"""
|
||||
)
|
||||
elif state == QValidator.State.Intermediate:
|
||||
elif state == QValidator.State.Intermediate and self.intermediate:
|
||||
self.parent.setToolTip(reason)
|
||||
self.parent.setStyleSheet(
|
||||
"""
|
||||
|
|
@ -469,42 +603,66 @@ class PathValidator(QValidator):
|
|||
"""
|
||||
)
|
||||
|
||||
return QValidator.State.Acceptable, path, len(path)
|
||||
return QValidator.State.Acceptable, mongo, len(mongo)
|
||||
|
||||
def validate(self, path: str, pos: int) -> (QValidator.State, str, int):
|
||||
"""Validate entered path.
|
||||
def validate(self, mongo: str, pos: int) -> (QValidator.State, str, int): # noqa
|
||||
"""Validate entered mongodb connection string.
|
||||
|
||||
It can be regular path - in that case we test if it does exist.
|
||||
It can also be mongodb connection string. In that case we parse it
|
||||
as url (it should start with `mongodb://` url schema.
|
||||
As url (it should start with `mongodb://` or
|
||||
`mongodb+srv:// url schema.
|
||||
|
||||
Args:
|
||||
path (str): path, connection string url or pype token.
|
||||
mongo (str): connection string url.
|
||||
pos (int): current position.
|
||||
|
||||
Todo:
|
||||
It can also be Pype token, binding it to Pype user account.
|
||||
Returns:
|
||||
(QValidator.State.Acceptable, str, int):
|
||||
Indicate input state with color and always return
|
||||
Acceptable state as we need to be able to edit input further.
|
||||
|
||||
"""
|
||||
if path.startswith("mongodb"):
|
||||
pos = len(path)
|
||||
if not mongo.startswith("mongodb"):
|
||||
return self._return_state(
|
||||
QValidator.State.Intermediate, "", path, pos)
|
||||
QValidator.State.Invalid, "need mongodb schema", mongo)
|
||||
|
||||
if len(path) < 6:
|
||||
return self._return_state(
|
||||
QValidator.State.Intermediate, "", path, pos)
|
||||
return self._return_state(
|
||||
QValidator.State.Intermediate, "", mongo)
|
||||
|
||||
valid, reason = validate_path_string(path)
|
||||
if not valid:
|
||||
|
||||
class PathValidator(MongoValidator):
|
||||
"""Validate mongodb url for Qt widgets."""
|
||||
|
||||
def validate(self, path: str, pos: int) -> (QValidator.State, str, int): # noqa
|
||||
"""Validate path to be accepted by Igniter.
|
||||
|
||||
Args:
|
||||
path (str): path to Pype.
|
||||
pos (int): current position.
|
||||
|
||||
Returns:
|
||||
(QValidator.State.Acceptable, str, int):
|
||||
Indicate input state with color and always return
|
||||
Acceptable state as we need to be able to edit input further.
|
||||
|
||||
"""
|
||||
# allow empty path as that will use current version coming with
|
||||
# Pype Igniter
|
||||
if len(path) == 0:
|
||||
return self._return_state(
|
||||
QValidator.State.Invalid, reason, path, pos)
|
||||
else:
|
||||
return self._return_state(
|
||||
QValidator.State.Acceptable, reason, path, pos)
|
||||
QValidator.State.Acceptable, "Use version with Igniter", path)
|
||||
|
||||
if len(path) > 3:
|
||||
valid, reason = validate_path_string(path)
|
||||
if not valid:
|
||||
return self._return_state(
|
||||
QValidator.State.Invalid, reason, path)
|
||||
else:
|
||||
return self._return_state(
|
||||
QValidator.State.Acceptable, reason, path)
|
||||
|
||||
|
||||
class CollapsibleWidget(QtWidgets.QWidget):
|
||||
"""Collapsible widget to hide mongo url in necessary."""
|
||||
|
||||
def __init__(self, parent=None, title: str = "", animation: int = 300):
|
||||
self._mainLayout = QtWidgets.QGridLayout(parent)
|
||||
|
|
@ -515,9 +673,9 @@ class CollapsibleWidget(QtWidgets.QWidget):
|
|||
self._animation = animation
|
||||
self._title = title
|
||||
super(CollapsibleWidget, self).__init__(parent)
|
||||
self._initUi()
|
||||
self._init_ui()
|
||||
|
||||
def _initUi(self):
|
||||
def _init_ui(self):
|
||||
self._toggleButton.setStyleSheet(
|
||||
"""QToolButton {
|
||||
border: none;
|
||||
|
|
@ -576,13 +734,13 @@ class CollapsibleWidget(QtWidgets.QWidget):
|
|||
self._toggleAnimation.setDirection(direction)
|
||||
self._toggleAnimation.start()
|
||||
|
||||
def setContentLayout(self, content_layout: QtWidgets.QLayout):
|
||||
def setContentLayout(self, content_layout: QtWidgets.QLayout): # noqa
|
||||
self._contentArea.setLayout(content_layout)
|
||||
collapsed_height = \
|
||||
self.sizeHint().height() - self._contentArea.maximumHeight()
|
||||
content_height = self._contentArea.sizeHint().height()
|
||||
|
||||
for i in range(0, self._toggleAnimation.animationCount() - 1):
|
||||
for i in range(self._toggleAnimation.animationCount() - 1):
|
||||
sec_anim = self._toggleAnimation.animationAt(i)
|
||||
sec_anim.setDuration(self._animation)
|
||||
sec_anim.setStartValue(collapsed_height)
|
||||
|
|
@ -593,7 +751,7 @@ class CollapsibleWidget(QtWidgets.QWidget):
|
|||
|
||||
con_anim.setDuration(self._animation)
|
||||
con_anim.setStartValue(0)
|
||||
con_anim.setEndValue(32)
|
||||
con_anim.setEndValue(collapsed_height + content_height)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -2,15 +2,27 @@
|
|||
"""Working thread for installer."""
|
||||
import os
|
||||
import sys
|
||||
from zipfile import ZipFile
|
||||
from pathlib import Path
|
||||
|
||||
from Qt.QtCore import QThread, Signal
|
||||
from Qt.QtCore import QThread, Signal, QObject # noqa
|
||||
|
||||
from .bootstrap_repos import (
|
||||
BootstrapRepos,
|
||||
PypeVersionInvalid,
|
||||
PypeVersionIOError,
|
||||
PypeVersionExists,
|
||||
PypeVersion
|
||||
)
|
||||
|
||||
from .bootstrap_repos import BootstrapRepos
|
||||
from .bootstrap_repos import PypeVersion
|
||||
from .tools import validate_mongo_connection
|
||||
|
||||
|
||||
class InstallResult(QObject):
|
||||
"""Used to pass results back."""
|
||||
def __init__(self, value):
|
||||
self.status = value
|
||||
|
||||
|
||||
class InstallThread(QThread):
|
||||
"""Install Worker thread.
|
||||
|
||||
|
|
@ -21,19 +33,18 @@ class InstallThread(QThread):
|
|||
If path contains plain repositories, they are zipped and installed to
|
||||
user data dir.
|
||||
|
||||
Attributes:
|
||||
progress (Signal): signal reporting progress back o UI.
|
||||
message (Signal): message displaying in UI console.
|
||||
|
||||
"""
|
||||
|
||||
progress = Signal(int)
|
||||
message = Signal((str, bool))
|
||||
finished = Signal(object)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
def __init__(self, callback, parent=None,):
|
||||
self._mongo = None
|
||||
self._path = None
|
||||
self.result_callback = callback
|
||||
|
||||
QThread.__init__(self, parent)
|
||||
self.finished.connect(callback)
|
||||
|
||||
def run(self):
|
||||
"""Thread entry point.
|
||||
|
|
@ -64,10 +75,12 @@ class InstallThread(QThread):
|
|||
except ValueError:
|
||||
self.message.emit(
|
||||
"!!! We need MongoDB URL to proceed.", True)
|
||||
self.finished.emit(InstallResult(-1))
|
||||
return
|
||||
else:
|
||||
self._mongo = os.getenv("PYPE_MONGO")
|
||||
else:
|
||||
self.message.emit("Saving mongo connection string ...", False)
|
||||
bs.registry.set_secure_item("pypeMongo", self._mongo)
|
||||
|
||||
os.environ["PYPE_MONGO"] = self._mongo
|
||||
|
|
@ -77,7 +90,8 @@ class InstallThread(QThread):
|
|||
detected = bs.find_pype(include_zips=True)
|
||||
|
||||
if detected:
|
||||
if PypeVersion(version=local_version) < detected[-1]:
|
||||
if PypeVersion(
|
||||
version=local_version, path=Path()) < detected[-1]:
|
||||
self.message.emit((
|
||||
f"Latest installed version {detected[-1]} is newer "
|
||||
f"then currently running {local_version}"
|
||||
|
|
@ -85,14 +99,16 @@ class InstallThread(QThread):
|
|||
self.message.emit("Skipping Pype install ...", False)
|
||||
if detected[-1].path.suffix.lower() == ".zip":
|
||||
bs.extract_pype(detected[-1])
|
||||
self.finished.emit(InstallResult(0))
|
||||
return
|
||||
|
||||
if PypeVersion(version=local_version) == detected[-1]:
|
||||
if PypeVersion(version=local_version).get_main_version() == detected[-1].get_main_version(): # noqa
|
||||
self.message.emit((
|
||||
f"Latest installed version is the same as "
|
||||
f"currently running {local_version}"
|
||||
), False)
|
||||
self.message.emit("Skipping Pype install ...", False)
|
||||
self.finished.emit(InstallResult(0))
|
||||
return
|
||||
|
||||
self.message.emit((
|
||||
|
|
@ -100,11 +116,21 @@ class InstallThread(QThread):
|
|||
f"currently running one {local_version}"
|
||||
), False)
|
||||
else:
|
||||
# we cannot build install package from frozen code.
|
||||
if getattr(sys, 'frozen', False):
|
||||
self.message.emit("None detected.", True)
|
||||
self.message.emit(("Please set path to Pype sources to "
|
||||
"build installation."), False)
|
||||
self.message.emit(("We will use Pype coming with "
|
||||
"installer."), False)
|
||||
pype_version = bs.create_version_from_frozen_code()
|
||||
if not pype_version:
|
||||
self.message.emit(
|
||||
f"!!! Install failed - {pype_version}", True)
|
||||
self.finished.emit(InstallResult(-1))
|
||||
return
|
||||
self.message.emit(f"Using: {pype_version}", False)
|
||||
bs.install_version(pype_version)
|
||||
self.message.emit(f"Installed as {pype_version}", False)
|
||||
self.progress.emit(100)
|
||||
self.finished.emit(InstallResult(1))
|
||||
return
|
||||
else:
|
||||
self.message.emit("None detected.", False)
|
||||
|
|
@ -112,31 +138,26 @@ class InstallThread(QThread):
|
|||
self.message.emit(
|
||||
f"We will use local Pype version {local_version}", False)
|
||||
|
||||
repo_file = bs.install_live_repos()
|
||||
if not repo_file:
|
||||
local_pype = bs.create_version_from_live_code()
|
||||
if not local_pype:
|
||||
self.message.emit(
|
||||
f"!!! Install failed - {repo_file}", True)
|
||||
f"!!! Install failed - {local_pype}", True)
|
||||
self.finished.emit(InstallResult(-1))
|
||||
return
|
||||
|
||||
destination = bs.data_dir / repo_file.stem
|
||||
if destination.exists():
|
||||
try:
|
||||
destination.unlink()
|
||||
except OSError as e:
|
||||
self.message.emit(
|
||||
f"!!! Cannot remove already existing {destination}",
|
||||
True)
|
||||
self.message.emit(e.strerror, True)
|
||||
return
|
||||
try:
|
||||
bs.install_version(local_pype)
|
||||
except (PypeVersionExists,
|
||||
PypeVersionInvalid,
|
||||
PypeVersionIOError) as e:
|
||||
self.message.emit(f"Installed failed: ", True)
|
||||
self.message.emit(str(e), True)
|
||||
self.finished.emit(InstallResult(-1))
|
||||
return
|
||||
|
||||
destination.mkdir(parents=True)
|
||||
|
||||
# extract zip there
|
||||
self.message.emit("Extracting zip to destination ...", False)
|
||||
with ZipFile(repo_file, "r") as zip_ref:
|
||||
zip_ref.extractall(destination)
|
||||
|
||||
self.message.emit(f"Installed as {repo_file}", False)
|
||||
self.message.emit(f"Installed as {local_pype}", False)
|
||||
self.progress.emit(100)
|
||||
return
|
||||
else:
|
||||
# if we have mongo connection string, validate it, set it to
|
||||
# user settings and get PYPE_PATH from there.
|
||||
|
|
@ -144,25 +165,46 @@ class InstallThread(QThread):
|
|||
if not validate_mongo_connection(self._mongo):
|
||||
self.message.emit(
|
||||
f"!!! invalid mongo url {self._mongo}", True)
|
||||
self.finished.emit(InstallResult(-1))
|
||||
return
|
||||
bs.registry.set_secure_item("pypeMongo", self._mongo)
|
||||
os.environ["PYPE_MONGO"] = self._mongo
|
||||
|
||||
if os.getenv("PYPE_PATH") == self._path:
|
||||
...
|
||||
|
||||
self.message.emit(f"processing {self._path}", True)
|
||||
repo_file = bs.process_entered_location(self._path)
|
||||
|
||||
if not repo_file:
|
||||
self.message.emit("!!! Cannot install", True)
|
||||
self.finished.emit(InstallResult(-1))
|
||||
return
|
||||
|
||||
self.progress.emit(100)
|
||||
self.finished.emit(InstallResult(1))
|
||||
return
|
||||
|
||||
def set_path(self, path: str) -> None:
|
||||
"""Helper to set path.
|
||||
|
||||
Args:
|
||||
path (str): Path to set.
|
||||
|
||||
"""
|
||||
self._path = path
|
||||
|
||||
def set_mongo(self, mongo: str) -> None:
|
||||
"""Helper to set mongo url.
|
||||
|
||||
Args:
|
||||
mongo (str): Mongodb url.
|
||||
|
||||
"""
|
||||
self._mongo = mongo
|
||||
|
||||
def set_progress(self, progress: int) -> None:
|
||||
"""Helper to set progress bar.
|
||||
|
||||
Args:
|
||||
progress (int): Progress in percents.
|
||||
|
||||
"""
|
||||
self.progress.emit(progress)
|
||||
|
|
|
|||
109
igniter/tools.py
109
igniter/tools.py
|
|
@ -6,11 +6,10 @@ Functions ``compose_url()`` and ``decompose_url()`` are the same as in
|
|||
version is decided.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import uuid
|
||||
from typing import Dict
|
||||
from typing import Dict, Union
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
from pathlib import Path
|
||||
import platform
|
||||
|
||||
from pymongo import MongoClient
|
||||
from pymongo.errors import ServerSelectionTimeoutError, InvalidURI
|
||||
|
|
@ -115,10 +114,14 @@ def validate_mongo_connection(cnx: str) -> (bool, str):
|
|||
if parsed.scheme not in ["mongodb", "mongodb+srv"]:
|
||||
return False, "Not mongodb schema"
|
||||
# we have mongo connection string. Let's try if we can connect.
|
||||
components = decompose_url(cnx)
|
||||
try:
|
||||
components = decompose_url(cnx)
|
||||
except RuntimeError:
|
||||
return False, f"Invalid port specified."
|
||||
|
||||
mongo_args = {
|
||||
"host": compose_url(**components),
|
||||
"serverSelectionTimeoutMS": 1000
|
||||
"serverSelectionTimeoutMS": 2000
|
||||
}
|
||||
port = components.get("port")
|
||||
if port is not None:
|
||||
|
|
@ -137,13 +140,32 @@ def validate_mongo_connection(cnx: str) -> (bool, str):
|
|||
return True, "Connection is successful"
|
||||
|
||||
|
||||
def validate_path_string(path: str) -> (bool, str):
|
||||
"""Validate string if it is acceptable by **Igniter**.
|
||||
|
||||
`path` string can be either regular path, or mongodb url or Pype token.
|
||||
def validate_mongo_string(mongo: str) -> (bool, str):
|
||||
"""Validate string if it is mongo url acceptable by **Igniter**..
|
||||
|
||||
Args:
|
||||
path (str): String to validate.
|
||||
mongo (str): String to validate.
|
||||
|
||||
Returns:
|
||||
(bool, str):
|
||||
True if valid, False if not and in second part of tuple
|
||||
the reason why it failed.
|
||||
|
||||
"""
|
||||
if not mongo:
|
||||
return True, "empty string"
|
||||
parsed = urlparse(mongo)
|
||||
if parsed.scheme in ["mongodb", "mongodb+srv"]:
|
||||
return validate_mongo_connection(mongo)
|
||||
return False, "not valid mongodb schema"
|
||||
|
||||
|
||||
def validate_path_string(path: str) -> (bool, str):
|
||||
"""Validate string if it is path to Pype repository.
|
||||
|
||||
Args:
|
||||
path (str): Path to validate.
|
||||
|
||||
|
||||
Returns:
|
||||
(bool, str):
|
||||
|
|
@ -152,22 +174,15 @@ def validate_path_string(path: str) -> (bool, str):
|
|||
|
||||
"""
|
||||
if not path:
|
||||
return True, "Empty string"
|
||||
parsed = urlparse(path)
|
||||
if parsed.scheme == "mongodb":
|
||||
return validate_mongo_connection(path)
|
||||
# test for uuid
|
||||
try:
|
||||
uuid.UUID(path)
|
||||
except (ValueError, TypeError):
|
||||
# not uuid
|
||||
if not os.path.exists(path):
|
||||
return False, "Path doesn't exist or invalid token"
|
||||
return True, "Path exists"
|
||||
else:
|
||||
# we have pype token
|
||||
# todo: implement
|
||||
return False, "Not implemented yet"
|
||||
return False, "empty string"
|
||||
|
||||
if not Path(path).exists():
|
||||
return False, "path doesn't exists"
|
||||
|
||||
if not Path(path).is_dir():
|
||||
return False, "path is not directory"
|
||||
|
||||
return True, "valid path"
|
||||
|
||||
|
||||
def load_environments(sections: list = None) -> dict:
|
||||
|
|
@ -200,3 +215,43 @@ def load_environments(sections: list = None) -> dict:
|
|||
merged_env = acre.append(merged_env, parsed_env)
|
||||
|
||||
return acre.compute(merged_env, cleanup=True)
|
||||
|
||||
|
||||
def get_pype_path_from_db(url: str) -> Union[str, None]:
|
||||
"""Get Pype path from database.
|
||||
|
||||
We are loading data from database `pype` and collection `settings`.
|
||||
There we expect document type `global_settings`.
|
||||
|
||||
Args:
|
||||
url (str): mongodb url.
|
||||
|
||||
Returns:
|
||||
path to Pype or None if not found
|
||||
|
||||
"""
|
||||
try:
|
||||
components = decompose_url(url)
|
||||
except RuntimeError:
|
||||
return None
|
||||
mongo_args = {
|
||||
"host": compose_url(**components),
|
||||
"serverSelectionTimeoutMS": 2000
|
||||
}
|
||||
port = components.get("port")
|
||||
if port is not None:
|
||||
mongo_args["port"] = int(port)
|
||||
|
||||
try:
|
||||
client = MongoClient(**mongo_args)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
db = client.pype
|
||||
col = db.settings
|
||||
|
||||
global_settings = col.find_one({"type": "global_settings"}, {"data": 1})
|
||||
if not global_settings:
|
||||
return None
|
||||
global_settings.get("data", {})
|
||||
return global_settings.get("pype_path", {}).get(platform.system().lower())
|
||||
|
|
|
|||
4
igniter/version.py
Normal file
4
igniter/version.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Definition of Igniter version."""
|
||||
|
||||
__version__ = "1.0.0"
|
||||
336
poetry.lock
generated
336
poetry.lock
generated
|
|
@ -80,7 +80,7 @@ python-dateutil = ">=2.7.0"
|
|||
|
||||
[[package]]
|
||||
name = "astroid"
|
||||
version = "2.5"
|
||||
version = "2.5.1"
|
||||
description = "An abstract syntax tree for Python with inference support."
|
||||
category = "dev"
|
||||
optional = false
|
||||
|
|
@ -234,7 +234,7 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"]
|
|||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "5.4"
|
||||
version = "5.5"
|
||||
description = "Code coverage measurement for Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
|
|
@ -273,6 +273,21 @@ python-versions = ">=3.6"
|
|||
[package.dependencies]
|
||||
importlib-metadata = ">=3.1.1"
|
||||
|
||||
[[package]]
|
||||
name = "dnspython"
|
||||
version = "2.1.0"
|
||||
description = "DNS toolkit"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
dnssec = ["cryptography (>=2.6)"]
|
||||
doh = ["requests", "requests-toolbelt"]
|
||||
idna = ["idna (>=2.1)"]
|
||||
curio = ["curio (>=1.2)", "sniffio (>=1.1)"]
|
||||
trio = ["trio (>=0.14.0)", "sniffio (>=1.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "docutils"
|
||||
version = "0.16"
|
||||
|
|
@ -331,7 +346,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
|||
|
||||
[[package]]
|
||||
name = "google-api-core"
|
||||
version = "1.26.0"
|
||||
version = "1.26.1"
|
||||
description = "Google API client core library"
|
||||
category = "main"
|
||||
optional = false
|
||||
|
|
@ -369,7 +384,7 @@ uritemplate = ">=3.0.0,<4dev"
|
|||
|
||||
[[package]]
|
||||
name = "google-auth"
|
||||
version = "1.27.0"
|
||||
version = "1.27.1"
|
||||
description = "Google Authentication Library"
|
||||
category = "main"
|
||||
optional = false
|
||||
|
|
@ -387,7 +402,7 @@ pyopenssl = ["pyopenssl (>=20.0.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "google-auth-httplib2"
|
||||
version = "0.0.4"
|
||||
version = "0.1.0"
|
||||
description = "Google Authentication Library: httplib2 transport"
|
||||
category = "main"
|
||||
optional = false
|
||||
|
|
@ -395,7 +410,7 @@ python-versions = "*"
|
|||
|
||||
[package.dependencies]
|
||||
google-auth = "*"
|
||||
httplib2 = ">=0.9.1"
|
||||
httplib2 = ">=0.15.0"
|
||||
six = "*"
|
||||
|
||||
[[package]]
|
||||
|
|
@ -455,6 +470,14 @@ zipp = ">=0.5"
|
|||
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "1.1.1"
|
||||
description = "iniconfig: brain-dead simple config-ini parsing"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "isort"
|
||||
version = "5.7.0"
|
||||
|
|
@ -538,7 +561,7 @@ format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator
|
|||
|
||||
[[package]]
|
||||
name = "keyring"
|
||||
version = "22.2.0"
|
||||
version = "22.3.0"
|
||||
description = "Store and access your passwords safely."
|
||||
category = "main"
|
||||
optional = false
|
||||
|
|
@ -589,14 +612,6 @@ category = "dev"
|
|||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "more-itertools"
|
||||
version = "8.7.0"
|
||||
description = "More routines for operating on iterables, beyond itertools"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "multidict"
|
||||
version = "5.1.0"
|
||||
|
|
@ -661,7 +676,7 @@ six = "*"
|
|||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "8.1.0"
|
||||
version = "8.1.1"
|
||||
description = "Python Imaging Library (Fork)"
|
||||
category = "main"
|
||||
optional = false
|
||||
|
|
@ -683,7 +698,7 @@ dev = ["pre-commit", "tox"]
|
|||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "3.15.3"
|
||||
version = "3.15.5"
|
||||
description = "Protocol Buffers"
|
||||
category = "main"
|
||||
optional = false
|
||||
|
|
@ -781,21 +796,21 @@ python-versions = ">=3.5"
|
|||
|
||||
[[package]]
|
||||
name = "pylint"
|
||||
version = "2.7.1"
|
||||
version = "2.7.2"
|
||||
description = "python code static checker"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "~=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
astroid = "2.5.0"
|
||||
astroid = ">=2.5.1,<2.6"
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
isort = ">=4.2.5,<6"
|
||||
mccabe = ">=0.6,<0.7"
|
||||
toml = ">=0.7.1"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=3.2,<4.0)", "python-docs-theme"]
|
||||
docs = ["sphinx (==3.5.1)", "python-docs-theme (==2020.12)"]
|
||||
|
||||
[[package]]
|
||||
name = "pymongo"
|
||||
|
|
@ -906,25 +921,24 @@ python-versions = ">=3.5"
|
|||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "5.4.3"
|
||||
version = "6.2.2"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
|
||||
attrs = ">=17.4.0"
|
||||
attrs = ">=19.2.0"
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
|
||||
more-itertools = ">=4.0.0"
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<1.0"
|
||||
py = ">=1.5.0"
|
||||
wcwidth = "*"
|
||||
pluggy = ">=0.12,<1.0.0a1"
|
||||
py = ">=1.8.2"
|
||||
toml = "*"
|
||||
|
||||
[package.extras]
|
||||
checkqa-mypy = ["mypy (==v0.761)"]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1314,8 +1328,8 @@ python-versions = "*"
|
|||
|
||||
[[package]]
|
||||
name = "websocket-client"
|
||||
version = "0.57.0"
|
||||
description = "WebSocket client for Python. hybi13 is supported."
|
||||
version = "0.58.0"
|
||||
description = "WebSocket client for Python with low level API options"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
|
@ -1363,20 +1377,20 @@ typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""}
|
|||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.4.0"
|
||||
version = "3.4.1"
|
||||
description = "Backport of pathlib-compatible object wrapper for zip files"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
|
||||
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
|
||||
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
|
||||
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "3.7.*"
|
||||
content-hash = "22fb546eefa7ced2769c1dcb3220fd2ef4b22f0dc8436e023774e50e2ee6bde1"
|
||||
content-hash = "4905515073ad2bf2a8517d513d68e48669b6a829f24e540b2dd60bc70cbea26b"
|
||||
|
||||
[metadata.files]
|
||||
acre = []
|
||||
|
|
@ -1440,8 +1454,8 @@ arrow = [
|
|||
{file = "arrow-0.17.0.tar.gz", hash = "sha256:ff08d10cda1d36c68657d6ad20d74fbea493d980f8b2d45344e00d6ed2bf6ed4"},
|
||||
]
|
||||
astroid = [
|
||||
{file = "astroid-2.5-py3-none-any.whl", hash = "sha256:87ae7f2398b8a0ae5638ddecf9987f081b756e0e9fc071aeebdca525671fc4dc"},
|
||||
{file = "astroid-2.5.tar.gz", hash = "sha256:b31c92f545517dcc452f284bc9c044050862fbe6d93d2b3de4a215a6b384bf0d"},
|
||||
{file = "astroid-2.5.1-py3-none-any.whl", hash = "sha256:21d735aab248253531bb0f1e1e6d068f0ee23533e18ae8a6171ff892b98297cf"},
|
||||
{file = "astroid-2.5.1.tar.gz", hash = "sha256:cfc35498ee64017be059ceffab0a25bedf7548ab76f2bea691c5565896e7128d"},
|
||||
]
|
||||
async-timeout = [
|
||||
{file = "async-timeout-3.0.1.tar.gz", hash = "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f"},
|
||||
|
|
@ -1535,55 +1549,58 @@ commonmark = [
|
|||
{file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"},
|
||||
]
|
||||
coverage = [
|
||||
{file = "coverage-5.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:6d9c88b787638a451f41f97446a1c9fd416e669b4d9717ae4615bd29de1ac135"},
|
||||
{file = "coverage-5.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:66a5aae8233d766a877c5ef293ec5ab9520929c2578fd2069308a98b7374ea8c"},
|
||||
{file = "coverage-5.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9754a5c265f991317de2bac0c70a746efc2b695cf4d49f5d2cddeac36544fb44"},
|
||||
{file = "coverage-5.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:fbb17c0d0822684b7d6c09915677a32319f16ff1115df5ec05bdcaaee40b35f3"},
|
||||
{file = "coverage-5.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:b7f7421841f8db443855d2854e25914a79a1ff48ae92f70d0a5c2f8907ab98c9"},
|
||||
{file = "coverage-5.4-cp27-cp27m-win32.whl", hash = "sha256:4a780807e80479f281d47ee4af2eb2df3e4ccf4723484f77da0bb49d027e40a1"},
|
||||
{file = "coverage-5.4-cp27-cp27m-win_amd64.whl", hash = "sha256:87c4b38288f71acd2106f5d94f575bc2136ea2887fdb5dfe18003c881fa6b370"},
|
||||
{file = "coverage-5.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:c6809ebcbf6c1049002b9ac09c127ae43929042ec1f1dbd8bb1615f7cd9f70a0"},
|
||||
{file = "coverage-5.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ba7ca81b6d60a9f7a0b4b4e175dcc38e8fef4992673d9d6e6879fd6de00dd9b8"},
|
||||
{file = "coverage-5.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:89fc12c6371bf963809abc46cced4a01ca4f99cba17be5e7d416ed7ef1245d19"},
|
||||
{file = "coverage-5.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a8eb7785bd23565b542b01fb39115a975fefb4a82f23d407503eee2c0106247"},
|
||||
{file = "coverage-5.4-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:7e40d3f8eb472c1509b12ac2a7e24158ec352fc8567b77ab02c0db053927e339"},
|
||||
{file = "coverage-5.4-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1ccae21a076d3d5f471700f6d30eb486da1626c380b23c70ae32ab823e453337"},
|
||||
{file = "coverage-5.4-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:755c56beeacac6a24c8e1074f89f34f4373abce8b662470d3aa719ae304931f3"},
|
||||
{file = "coverage-5.4-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:322549b880b2d746a7672bf6ff9ed3f895e9c9f108b714e7360292aa5c5d7cf4"},
|
||||
{file = "coverage-5.4-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:60a3307a84ec60578accd35d7f0c71a3a971430ed7eca6567399d2b50ef37b8c"},
|
||||
{file = "coverage-5.4-cp35-cp35m-win32.whl", hash = "sha256:1375bb8b88cb050a2d4e0da901001347a44302aeadb8ceb4b6e5aa373b8ea68f"},
|
||||
{file = "coverage-5.4-cp35-cp35m-win_amd64.whl", hash = "sha256:16baa799ec09cc0dcb43a10680573269d407c159325972dd7114ee7649e56c66"},
|
||||
{file = "coverage-5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2f2cf7a42d4b7654c9a67b9d091ec24374f7c58794858bff632a2039cb15984d"},
|
||||
{file = "coverage-5.4-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:b62046592b44263fa7570f1117d372ae3f310222af1fc1407416f037fb3af21b"},
|
||||
{file = "coverage-5.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:812eaf4939ef2284d29653bcfee9665f11f013724f07258928f849a2306ea9f9"},
|
||||
{file = "coverage-5.4-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:859f0add98707b182b4867359e12bde806b82483fb12a9ae868a77880fc3b7af"},
|
||||
{file = "coverage-5.4-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:04b14e45d6a8e159c9767ae57ecb34563ad93440fc1b26516a89ceb5b33c1ad5"},
|
||||
{file = "coverage-5.4-cp36-cp36m-win32.whl", hash = "sha256:ebfa374067af240d079ef97b8064478f3bf71038b78b017eb6ec93ede1b6bcec"},
|
||||
{file = "coverage-5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:84df004223fd0550d0ea7a37882e5c889f3c6d45535c639ce9802293b39cd5c9"},
|
||||
{file = "coverage-5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1b811662ecf72eb2d08872731636aee6559cae21862c36f74703be727b45df90"},
|
||||
{file = "coverage-5.4-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6b588b5cf51dc0fd1c9e19f622457cc74b7d26fe295432e434525f1c0fae02bc"},
|
||||
{file = "coverage-5.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:3fe50f1cac369b02d34ad904dfe0771acc483f82a1b54c5e93632916ba847b37"},
|
||||
{file = "coverage-5.4-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:32ab83016c24c5cf3db2943286b85b0a172dae08c58d0f53875235219b676409"},
|
||||
{file = "coverage-5.4-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:68fb816a5dd901c6aff352ce49e2a0ffadacdf9b6fae282a69e7a16a02dad5fb"},
|
||||
{file = "coverage-5.4-cp37-cp37m-win32.whl", hash = "sha256:a636160680c6e526b84f85d304e2f0bb4e94f8284dd765a1911de9a40450b10a"},
|
||||
{file = "coverage-5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:bb32ca14b4d04e172c541c69eec5f385f9a075b38fb22d765d8b0ce3af3a0c22"},
|
||||
{file = "coverage-5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4d7165a4e8f41eca6b990c12ee7f44fef3932fac48ca32cecb3a1b2223c21f"},
|
||||
{file = "coverage-5.4-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a565f48c4aae72d1d3d3f8e8fb7218f5609c964e9c6f68604608e5958b9c60c3"},
|
||||
{file = "coverage-5.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fff1f3a586246110f34dc762098b5afd2de88de507559e63553d7da643053786"},
|
||||
{file = "coverage-5.4-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a839e25f07e428a87d17d857d9935dd743130e77ff46524abb992b962eb2076c"},
|
||||
{file = "coverage-5.4-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:6625e52b6f346a283c3d563d1fd8bae8956daafc64bb5bbd2b8f8a07608e3994"},
|
||||
{file = "coverage-5.4-cp38-cp38-win32.whl", hash = "sha256:5bee3970617b3d74759b2d2df2f6a327d372f9732f9ccbf03fa591b5f7581e39"},
|
||||
{file = "coverage-5.4-cp38-cp38-win_amd64.whl", hash = "sha256:03ed2a641e412e42cc35c244508cf186015c217f0e4d496bf6d7078ebe837ae7"},
|
||||
{file = "coverage-5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:14a9f1887591684fb59fdba8feef7123a0da2424b0652e1b58dd5b9a7bb1188c"},
|
||||
{file = "coverage-5.4-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9564ac7eb1652c3701ac691ca72934dd3009997c81266807aef924012df2f4b3"},
|
||||
{file = "coverage-5.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:0f48fc7dc82ee14aeaedb986e175a429d24129b7eada1b7e94a864e4f0644dde"},
|
||||
{file = "coverage-5.4-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:107d327071061fd4f4a2587d14c389a27e4e5c93c7cba5f1f59987181903902f"},
|
||||
{file = "coverage-5.4-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:0cdde51bfcf6b6bd862ee9be324521ec619b20590787d1655d005c3fb175005f"},
|
||||
{file = "coverage-5.4-cp39-cp39-win32.whl", hash = "sha256:c67734cff78383a1f23ceba3b3239c7deefc62ac2b05fa6a47bcd565771e5880"},
|
||||
{file = "coverage-5.4-cp39-cp39-win_amd64.whl", hash = "sha256:c669b440ce46ae3abe9b2d44a913b5fd86bb19eb14a8701e88e3918902ecd345"},
|
||||
{file = "coverage-5.4-pp36-none-any.whl", hash = "sha256:c0ff1c1b4d13e2240821ef23c1efb1f009207cb3f56e16986f713c2b0e7cd37f"},
|
||||
{file = "coverage-5.4-pp37-none-any.whl", hash = "sha256:cd601187476c6bed26a0398353212684c427e10a903aeafa6da40c63309d438b"},
|
||||
{file = "coverage-5.4.tar.gz", hash = "sha256:6d2e262e5e8da6fa56e774fb8e2643417351427604c2b177f8e8c5f75fc928ca"},
|
||||
{file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"},
|
||||
{file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"},
|
||||
{file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"},
|
||||
{file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"},
|
||||
{file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"},
|
||||
{file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"},
|
||||
{file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"},
|
||||
{file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"},
|
||||
{file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"},
|
||||
{file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"},
|
||||
{file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"},
|
||||
{file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"},
|
||||
{file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"},
|
||||
{file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"},
|
||||
{file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"},
|
||||
{file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"},
|
||||
{file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"},
|
||||
{file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"},
|
||||
{file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"},
|
||||
{file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"},
|
||||
{file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"},
|
||||
{file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"},
|
||||
{file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"},
|
||||
{file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"},
|
||||
{file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"},
|
||||
{file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"},
|
||||
{file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"},
|
||||
{file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"},
|
||||
{file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"},
|
||||
{file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"},
|
||||
{file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"},
|
||||
{file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"},
|
||||
{file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"},
|
||||
{file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"},
|
||||
{file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"},
|
||||
{file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"},
|
||||
{file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"},
|
||||
{file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"},
|
||||
{file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"},
|
||||
{file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"},
|
||||
{file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"},
|
||||
{file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"},
|
||||
{file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"},
|
||||
{file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"},
|
||||
{file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"},
|
||||
{file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"},
|
||||
{file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"},
|
||||
{file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"},
|
||||
{file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"},
|
||||
{file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"},
|
||||
{file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"},
|
||||
{file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"},
|
||||
]
|
||||
cryptography = [
|
||||
{file = "cryptography-3.4.6-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:57ad77d32917bc55299b16d3b996ffa42a1c73c6cfa829b14043c561288d2799"},
|
||||
|
|
@ -1610,6 +1627,10 @@ cx-freeze = [
|
|||
{file = "cx_Freeze-6.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:507bbaace2fd27edb0e6b024898ab2e4831d45d7238264f578a5e4fa70f065e5"},
|
||||
{file = "cx_Freeze-6.5.3.tar.gz", hash = "sha256:e0d03cabcdf9b9c21354807ed9f06fa9481a8fd5a0838968a830f01a70820ff1"},
|
||||
]
|
||||
dnspython = [
|
||||
{file = "dnspython-2.1.0-py3-none-any.whl", hash = "sha256:95d12f6ef0317118d2a1a6fc49aac65ffec7eb8087474158f42f26a639135216"},
|
||||
{file = "dnspython-2.1.0.zip", hash = "sha256:e4a87f0b573201a0f3727fa18a516b055fd1107e0e5477cded4a2de497df1dd4"},
|
||||
]
|
||||
docutils = [
|
||||
{file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"},
|
||||
{file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"},
|
||||
|
|
@ -1629,20 +1650,20 @@ future = [
|
|||
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
|
||||
]
|
||||
google-api-core = [
|
||||
{file = "google-api-core-1.26.0.tar.gz", hash = "sha256:4230ec764d48ca934fe69b85cc217e31e844e176f68df93e252acd55350e730b"},
|
||||
{file = "google_api_core-1.26.0-py2.py3-none-any.whl", hash = "sha256:002e44c533299aecd9dd265d200f9eacd9957cddd2c72e2cd1cb5cea127e972d"},
|
||||
{file = "google-api-core-1.26.1.tar.gz", hash = "sha256:23b0df512c4cc8729793f8992edb350e3211f5fd0ec007afb1599864b421beef"},
|
||||
{file = "google_api_core-1.26.1-py2.py3-none-any.whl", hash = "sha256:c383206f0f87545d3e658c4f8dc3b18a8457610fdbd791a15757c5b42d1e0e7f"},
|
||||
]
|
||||
google-api-python-client = [
|
||||
{file = "google-api-python-client-1.12.8.tar.gz", hash = "sha256:f3b9684442eec2cfe9f9bb48e796ef919456b82142c7528c5fd527e5224f08bb"},
|
||||
{file = "google_api_python_client-1.12.8-py2.py3-none-any.whl", hash = "sha256:3c4c4ca46b5c21196bec7ee93453443e477d82cbfa79234d1ce0645f81170eaf"},
|
||||
]
|
||||
google-auth = [
|
||||
{file = "google-auth-1.27.0.tar.gz", hash = "sha256:da5218cbf33b8461d7661d6b4ad91c12c0107e2767904d5e3ae6408031d5463e"},
|
||||
{file = "google_auth-1.27.0-py2.py3-none-any.whl", hash = "sha256:d3640ea61ee025d5af00e3ffd82ba0a06dd99724adaf50bdd52f49daf29f3f65"},
|
||||
{file = "google-auth-1.27.1.tar.gz", hash = "sha256:d8958af6968e4ecd599f82357ebcfeb126f826ed0656126ad68416f810f7531e"},
|
||||
{file = "google_auth-1.27.1-py2.py3-none-any.whl", hash = "sha256:63a5636d7eacfe6ef5b7e36e112b3149fa1c5b5ad77dd6df54910459bcd6b89f"},
|
||||
]
|
||||
google-auth-httplib2 = [
|
||||
{file = "google-auth-httplib2-0.0.4.tar.gz", hash = "sha256:8d092cc60fb16517b12057ec0bba9185a96e3b7169d86ae12eae98e645b7bc39"},
|
||||
{file = "google_auth_httplib2-0.0.4-py2.py3-none-any.whl", hash = "sha256:aeaff501738b289717fac1980db9711d77908a6c227f60e4aa1923410b43e2ee"},
|
||||
{file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"},
|
||||
{file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"},
|
||||
]
|
||||
googleapis-common-protos = [
|
||||
{file = "googleapis-common-protos-1.53.0.tar.gz", hash = "sha256:a88ee8903aa0a81f6c3cec2d5cf62d3c8aa67c06439b0496b49048fb1854ebf4"},
|
||||
|
|
@ -1664,6 +1685,10 @@ importlib-metadata = [
|
|||
{file = "importlib_metadata-3.7.0-py3-none-any.whl", hash = "sha256:c6af5dbf1126cd959c4a8d8efd61d4d3c83bddb0459a17e554284a077574b614"},
|
||||
{file = "importlib_metadata-3.7.0.tar.gz", hash = "sha256:24499ffde1b80be08284100393955842be4a59c7c16bbf2738aad0e464a8e0aa"},
|
||||
]
|
||||
iniconfig = [
|
||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
||||
]
|
||||
isort = [
|
||||
{file = "isort-5.7.0-py3-none-any.whl", hash = "sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc"},
|
||||
{file = "isort-5.7.0.tar.gz", hash = "sha256:c729845434366216d320e936b8ad6f9d681aab72dc7cbc2d51bedc3582f3ad1e"},
|
||||
|
|
@ -1689,8 +1714,8 @@ jsonschema = [
|
|||
{file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"},
|
||||
]
|
||||
keyring = [
|
||||
{file = "keyring-22.2.0-py3-none-any.whl", hash = "sha256:8ae8e53d744e3e395e7402fd04c7474d46c8ad2d65e095bcde8a622dc643f7cd"},
|
||||
{file = "keyring-22.2.0.tar.gz", hash = "sha256:c73c66c4ca89bee6a233b1638e1d2f5bcba4da35f8713ad4f98decc46e64cccd"},
|
||||
{file = "keyring-22.3.0-py3-none-any.whl", hash = "sha256:2bc8363ebdd63886126a012057a85c8cb6e143877afa02619ac7dbc9f38a207b"},
|
||||
{file = "keyring-22.3.0.tar.gz", hash = "sha256:16927a444b2c73f983520a48dec79ddab49fe76429ea05b8d528d778c8339522"},
|
||||
]
|
||||
lazy-object-proxy = [
|
||||
{file = "lazy-object-proxy-1.5.2.tar.gz", hash = "sha256:5944a9b95e97de1980c65f03b79b356f30a43de48682b8bdd90aa5089f0ec1f4"},
|
||||
|
|
@ -1779,10 +1804,6 @@ mccabe = [
|
|||
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
|
||||
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
|
||||
]
|
||||
more-itertools = [
|
||||
{file = "more-itertools-8.7.0.tar.gz", hash = "sha256:c5d6da9ca3ff65220c3bfd2a8db06d698f05d4d2b9be57e1deb2be5a45019713"},
|
||||
{file = "more_itertools-8.7.0-py3-none-any.whl", hash = "sha256:5652a9ac72209ed7df8d9c15daf4e1aa0e3d2ccd3c87f8265a0673cd9cbc9ced"},
|
||||
]
|
||||
multidict = [
|
||||
{file = "multidict-5.1.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f"},
|
||||
{file = "multidict-5.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf"},
|
||||
|
|
@ -1836,64 +1857,65 @@ pathlib2 = [
|
|||
{file = "pathlib2-2.3.5.tar.gz", hash = "sha256:6cd9a47b597b37cc57de1c05e56fb1a1c9cc9fab04fe78c29acd090418529868"},
|
||||
]
|
||||
pillow = [
|
||||
{file = "Pillow-8.1.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:d355502dce85ade85a2511b40b4c61a128902f246504f7de29bbeec1ae27933a"},
|
||||
{file = "Pillow-8.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:93a473b53cc6e0b3ce6bf51b1b95b7b1e7e6084be3a07e40f79b42e83503fbf2"},
|
||||
{file = "Pillow-8.1.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2353834b2c49b95e1313fb34edf18fca4d57446675d05298bb694bca4b194174"},
|
||||
{file = "Pillow-8.1.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:1d208e670abfeb41b6143537a681299ef86e92d2a3dac299d3cd6830d5c7bded"},
|
||||
{file = "Pillow-8.1.0-cp36-cp36m-win32.whl", hash = "sha256:dd9eef866c70d2cbbea1ae58134eaffda0d4bfea403025f4db6859724b18ab3d"},
|
||||
{file = "Pillow-8.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:b09e10ec453de97f9a23a5aa5e30b334195e8d2ddd1ce76cc32e52ba63c8b31d"},
|
||||
{file = "Pillow-8.1.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:b02a0b9f332086657852b1f7cb380f6a42403a6d9c42a4c34a561aa4530d5234"},
|
||||
{file = "Pillow-8.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ca20739e303254287138234485579b28cb0d524401f83d5129b5ff9d606cb0a8"},
|
||||
{file = "Pillow-8.1.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:604815c55fd92e735f9738f65dabf4edc3e79f88541c221d292faec1904a4b17"},
|
||||
{file = "Pillow-8.1.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cf6e33d92b1526190a1de904df21663c46a456758c0424e4f947ae9aa6088bf7"},
|
||||
{file = "Pillow-8.1.0-cp37-cp37m-win32.whl", hash = "sha256:47c0d93ee9c8b181f353dbead6530b26980fe4f5485aa18be8f1fd3c3cbc685e"},
|
||||
{file = "Pillow-8.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:96d4dc103d1a0fa6d47c6c55a47de5f5dafd5ef0114fa10c85a1fd8e0216284b"},
|
||||
{file = "Pillow-8.1.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:7916cbc94f1c6b1301ac04510d0881b9e9feb20ae34094d3615a8a7c3db0dcc0"},
|
||||
{file = "Pillow-8.1.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3de6b2ee4f78c6b3d89d184ade5d8fa68af0848f9b6b6da2b9ab7943ec46971a"},
|
||||
{file = "Pillow-8.1.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cdbbe7dff4a677fb555a54f9bc0450f2a21a93c5ba2b44e09e54fcb72d2bd13d"},
|
||||
{file = "Pillow-8.1.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f50e7a98b0453f39000619d845be8b06e611e56ee6e8186f7f60c3b1e2f0feae"},
|
||||
{file = "Pillow-8.1.0-cp38-cp38-win32.whl", hash = "sha256:cb192176b477d49b0a327b2a5a4979552b7a58cd42037034316b8018ac3ebb59"},
|
||||
{file = "Pillow-8.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:6c5275bd82711cd3dcd0af8ce0bb99113ae8911fc2952805f1d012de7d600a4c"},
|
||||
{file = "Pillow-8.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6"},
|
||||
{file = "Pillow-8.1.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:5e2fe3bb2363b862671eba632537cd3a823847db4d98be95690b7e382f3d6378"},
|
||||
{file = "Pillow-8.1.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7612520e5e1a371d77e1d1ca3a3ee6227eef00d0a9cddb4ef7ecb0b7396eddf7"},
|
||||
{file = "Pillow-8.1.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d673c4990acd016229a5c1c4ee8a9e6d8f481b27ade5fc3d95938697fa443ce0"},
|
||||
{file = "Pillow-8.1.0-cp39-cp39-win32.whl", hash = "sha256:dc577f4cfdda354db3ae37a572428a90ffdbe4e51eda7849bf442fb803f09c9b"},
|
||||
{file = "Pillow-8.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:22d070ca2e60c99929ef274cfced04294d2368193e935c5d6febfd8b601bf865"},
|
||||
{file = "Pillow-8.1.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:a3d3e086474ef12ef13d42e5f9b7bbf09d39cf6bd4940f982263d6954b13f6a9"},
|
||||
{file = "Pillow-8.1.0-pp36-pypy36_pp73-manylinux2010_i686.whl", hash = "sha256:731ca5aabe9085160cf68b2dbef95fc1991015bc0a3a6ea46a371ab88f3d0913"},
|
||||
{file = "Pillow-8.1.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:bba80df38cfc17f490ec651c73bb37cd896bc2400cfba27d078c2135223c1206"},
|
||||
{file = "Pillow-8.1.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c3d911614b008e8a576b8e5303e3db29224b455d3d66d1b2848ba6ca83f9ece9"},
|
||||
{file = "Pillow-8.1.0-pp37-pypy37_pp73-manylinux2010_i686.whl", hash = "sha256:39725acf2d2e9c17356e6835dccebe7a697db55f25a09207e38b835d5e1bc032"},
|
||||
{file = "Pillow-8.1.0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:81c3fa9a75d9f1afafdb916d5995633f319db09bd773cb56b8e39f1e98d90820"},
|
||||
{file = "Pillow-8.1.0-pp37-pypy37_pp73-win32.whl", hash = "sha256:b6f00ad5ebe846cc91763b1d0c6d30a8042e02b2316e27b05de04fa6ec831ec5"},
|
||||
{file = "Pillow-8.1.0.tar.gz", hash = "sha256:887668e792b7edbfb1d3c9d8b5d8c859269a0f0eba4dda562adb95500f60dbba"},
|
||||
{file = "Pillow-8.1.1-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:14415e9e28410232370615dbde0cf0a00e526f522f665460344a5b96973a3086"},
|
||||
{file = "Pillow-8.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:924fc33cb4acaf6267b8ca3b8f1922620d57a28470d5e4f49672cea9a841eb08"},
|
||||
{file = "Pillow-8.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:df534e64d4f3e84e8f1e1a37da3f541555d947c1c1c09b32178537f0f243f69d"},
|
||||
{file = "Pillow-8.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:4fe74636ee71c57a7f65d7b21a9f127d842b4fb75511e5d256ace258826eb352"},
|
||||
{file = "Pillow-8.1.1-cp36-cp36m-win32.whl", hash = "sha256:3e759bcc03d6f39bc751e56d86bc87252b9a21c689a27c5ed753717a87d53a5b"},
|
||||
{file = "Pillow-8.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:292f2aa1ae5c5c1451cb4b558addb88c257411d3fd71c6cf45562911baffc979"},
|
||||
{file = "Pillow-8.1.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8211cac9bf10461f9e33fe9a3af6c5131f3fdd0d10672afc2abb2c70cf95c5ca"},
|
||||
{file = "Pillow-8.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:d30f30c044bdc0ab8f3924e1eeaac87e0ff8a27e87369c5cac4064b6ec78fd83"},
|
||||
{file = "Pillow-8.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:7094bbdecb95ebe53166e4c12cf5e28310c2b550b08c07c5dc15433898e2238e"},
|
||||
{file = "Pillow-8.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:1022f8f6dc3c5b0dcf928f1c49ba2ac73051f576af100d57776e2b65c1f76a8d"},
|
||||
{file = "Pillow-8.1.1-cp37-cp37m-win32.whl", hash = "sha256:a7d690b2c5f7e4a932374615fedceb1e305d2dd5363c1de15961725fe10e7d16"},
|
||||
{file = "Pillow-8.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:436b0a2dd9fe3f7aa6a444af6bdf53c1eb8f5ced9ea3ef104daa83f0ea18e7bc"},
|
||||
{file = "Pillow-8.1.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:c448d2b335e21951416a30cd48d35588d122a912d5fe9e41900afacecc7d21a1"},
|
||||
{file = "Pillow-8.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:bb18422ad00c1fecc731d06592e99c3be2c634da19e26942ba2f13d805005cf2"},
|
||||
{file = "Pillow-8.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:3ec87bd1248b23a2e4e19e774367fbe30fddc73913edc5f9b37470624f55dc1f"},
|
||||
{file = "Pillow-8.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:99ce3333b40b7a4435e0a18baad468d44ab118a4b1da0af0a888893d03253f1d"},
|
||||
{file = "Pillow-8.1.1-cp38-cp38-win32.whl", hash = "sha256:2f0d7034d5faae9a8d1019d152ede924f653df2ce77d3bba4ce62cd21b5f94ae"},
|
||||
{file = "Pillow-8.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:07872f1d8421db5a3fe770f7480835e5e90fddb58f36c216d4a2ac0d594de474"},
|
||||
{file = "Pillow-8.1.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:69da5b1d7102a61ce9b45deb2920a2012d52fd8f4201495ea9411d0071b0ec22"},
|
||||
{file = "Pillow-8.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2a40d7d4b17db87f5b9a1efc0aff56000e1d0d5ece415090c102aafa0ccbe858"},
|
||||
{file = "Pillow-8.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:01bb0a34f1a6689b138c0089d670ae2e8f886d2666a9b2f2019031abdea673c4"},
|
||||
{file = "Pillow-8.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:43b3c859912e8bf754b3c5142df624794b18eb7ae07cfeddc917e1a9406a3ef2"},
|
||||
{file = "Pillow-8.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:3b13d89d97b551e02549d1f0edf22bed6acfd6fd2e888cd1e9a953bf215f0e81"},
|
||||
{file = "Pillow-8.1.1-cp39-cp39-win32.whl", hash = "sha256:c143c409e7bc1db784471fe9d0bf95f37c4458e879ad84cfae640cb74ee11a26"},
|
||||
{file = "Pillow-8.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c5e3c36f02c815766ae9dd91899b1c5b4652f2a37b7a51609f3bd467c0f11fb"},
|
||||
{file = "Pillow-8.1.1-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:8cf77e458bd996dc85455f10fe443c0c946f5b13253773439bcbec08aa1aebc2"},
|
||||
{file = "Pillow-8.1.1-pp36-pypy36_pp73-manylinux2010_i686.whl", hash = "sha256:c10af40ee2f1a99e1ae755ab1f773916e8bca3364029a042cd9161c400416bd8"},
|
||||
{file = "Pillow-8.1.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:ff83dfeb04c98bb3e7948f876c17513a34e9a19fd92e292288649164924c1b39"},
|
||||
{file = "Pillow-8.1.1-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b9af590adc1e46898a1276527f3cfe2da8048ae43fbbf9b1bf9395f6c99d9b47"},
|
||||
{file = "Pillow-8.1.1-pp37-pypy37_pp73-manylinux2010_i686.whl", hash = "sha256:172acfaf00434a28dddfe592d83f2980e22e63c769ff4a448ddf7b7a38ffd165"},
|
||||
{file = "Pillow-8.1.1-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:33fdbd4f5608c852d97264f9d2e3b54e9e9959083d008145175b86100b275e5b"},
|
||||
{file = "Pillow-8.1.1-pp37-pypy37_pp73-win32.whl", hash = "sha256:59445af66b59cc39530b4f810776928d75e95f41e945f0c32a3de4aceb93c15d"},
|
||||
{file = "Pillow-8.1.1.tar.gz", hash = "sha256:f6fc18f9c9c7959bf58e6faf801d14fafb6d4717faaf6f79a68c8bb2a13dcf20"},
|
||||
]
|
||||
pluggy = [
|
||||
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
|
||||
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
|
||||
]
|
||||
protobuf = [
|
||||
{file = "protobuf-3.15.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:822341974a28d895f0b39df13b3e2f27577498c1d85b5e876ff1d53fbdf2ef97"},
|
||||
{file = "protobuf-3.15.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:31460706eb7c3bcc3c153877e580b78efa624b9626bd084fb882f20681ffa81a"},
|
||||
{file = "protobuf-3.15.3-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:405fbe27eeccd90b07e7cc20f2bcce477a86027435016aef71f15473dede92b5"},
|
||||
{file = "protobuf-3.15.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:a98195be29b2622961896893a6e92a4e0812e4e367078ac20f0c7982e51ff7ea"},
|
||||
{file = "protobuf-3.15.3-cp35-cp35m-win32.whl", hash = "sha256:7141c37a5af565908c3da10575c517c59a8e67591c507cf36f2655590200ddfc"},
|
||||
{file = "protobuf-3.15.3-cp35-cp35m-win_amd64.whl", hash = "sha256:ffc556af23c7e1278b43719999dd215619f73f8d42f40275c55a1de09938214f"},
|
||||
{file = "protobuf-3.15.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9aa4c216e48236c6af4e71f64afc0c13c12401d3067a323b9fe543bb676bac3"},
|
||||
{file = "protobuf-3.15.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0c678e11b7b6d6e5baa9710f44de01d48bc81b7db617ad5283a76f1f4c73df99"},
|
||||
{file = "protobuf-3.15.3-cp36-cp36m-win32.whl", hash = "sha256:1fe832e1a5c51c71c2d6e949e597f3c47ef39c817264086293e4037941ab9bd7"},
|
||||
{file = "protobuf-3.15.3-cp36-cp36m-win_amd64.whl", hash = "sha256:8a8157ff82760105cf435dbb8f4e7042a39c6d92f673fba8c2c815432b3f1063"},
|
||||
{file = "protobuf-3.15.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:970b9f648fc12d28ce6f1f10575bbf063e828e1fd8d95339602cad2312a4fefa"},
|
||||
{file = "protobuf-3.15.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:492beef9386706f84f683489fe18d1a7d8be5f4ab050782b3a484de1d1b01a69"},
|
||||
{file = "protobuf-3.15.3-cp37-cp37m-win32.whl", hash = "sha256:af760e4fe6f30e1af3d5dac6767444ff61ef621ac857b3405b8f3cd29f16ac55"},
|
||||
{file = "protobuf-3.15.3-cp37-cp37m-win_amd64.whl", hash = "sha256:21911af1fc692e7ca6a73c0fab3912a5d792ed7603350dbabd34a9722cbfe4d5"},
|
||||
{file = "protobuf-3.15.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a3e18453d91040dad1985d1ea8a237fb7522a84fcefc17b452f756833b066d71"},
|
||||
{file = "protobuf-3.15.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:d7761bd18fc3d197e50459c37abb95b64cd614e7b9014239a1e7c952433e380b"},
|
||||
{file = "protobuf-3.15.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f52c66f361de32096ba88c73ad0ff53585dafc569d8bf11968412175ddf297c"},
|
||||
{file = "protobuf-3.15.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3bbaed8e63cd62533a80adfa51f91b34bf7da29ac0335412dbebd21dac2d68b9"},
|
||||
{file = "protobuf-3.15.3-py2.py3-none-any.whl", hash = "sha256:ad8e808b572e6ee38131e7b58d94aa5c438e3a3469d055e8989ea73a8e2308c0"},
|
||||
{file = "protobuf-3.15.3.tar.gz", hash = "sha256:f3348af83391cdb842030e774d9bb01565ed4c62c93554cd1c69723411ec5e9d"},
|
||||
{file = "protobuf-3.15.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:d26ed8dbdbe6b62cd24173c9ceb7588ae7831eec172ac002b095af091db01196"},
|
||||
{file = "protobuf-3.15.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9133b39924485ae43c02fc8274e57e5aa1706ad0970de49c72cfb8c0854d5f89"},
|
||||
{file = "protobuf-3.15.5-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:6bb44c15c98091e926a98362bff7fb24338bdf4001a6614834b8414c3b8593ee"},
|
||||
{file = "protobuf-3.15.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:2d4cede5f2f2514df4a1eda1424a14d46daa5ea57963a1ea0fdab8d74ca2f9cd"},
|
||||
{file = "protobuf-3.15.5-cp35-cp35m-win32.whl", hash = "sha256:ab735b3a4342004afa60ff580ce2be0f2aa784f1f69ee7f08a23ef26d22d811d"},
|
||||
{file = "protobuf-3.15.5-cp35-cp35m-win_amd64.whl", hash = "sha256:a390e4bbb8232945fc8e4493c8b70949423a6dacee6f0353021b59c40b039e25"},
|
||||
{file = "protobuf-3.15.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dc7191b2e3361fdf2979e78a120a3a40e9d811318f6b2629036f53d9cb041c09"},
|
||||
{file = "protobuf-3.15.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:762f6b9fb8025db34f762a860fd2b1473dfc84bcd0c3e4f396a695c83d733729"},
|
||||
{file = "protobuf-3.15.5-cp36-cp36m-win32.whl", hash = "sha256:d1aab4d0aed36f7873734a243b46786d407cfa1010fae886249db56a1493a057"},
|
||||
{file = "protobuf-3.15.5-cp36-cp36m-win_amd64.whl", hash = "sha256:119b4d308c87e833b6265b3922d5f5927e9d804605fcb1c1f771aa4d17e03591"},
|
||||
{file = "protobuf-3.15.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c5b37b117ef89431149883d9b867c341a01f835142864722534885dcc1db6b1b"},
|
||||
{file = "protobuf-3.15.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f75aa0483fec2e4208bd4be18da0e3d7161dc74c65b6d6108f5968a8fe53a8ce"},
|
||||
{file = "protobuf-3.15.5-cp37-cp37m-win32.whl", hash = "sha256:5d52d89e26adf0ba65193b6be39025c7766740ccc57fe9d10ddb709220b360d9"},
|
||||
{file = "protobuf-3.15.5-cp37-cp37m-win_amd64.whl", hash = "sha256:87b5bc2ff944810a918628fc1f45f766acab23e1fecb0634fcf86cda554b30c4"},
|
||||
{file = "protobuf-3.15.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:282385b8dd168b0f71f2ffca74c1fb39377f42217830ab492a0b64cbe14f86c1"},
|
||||
{file = "protobuf-3.15.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9f4445f197f779cd5b37c9d5d4aeb0d1999c1df7d143a9bce21d03dac8dba205"},
|
||||
{file = "protobuf-3.15.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ac7c7a2b271307787ccdc0a45278827f36f72aba5040eadefff129b869068797"},
|
||||
{file = "protobuf-3.15.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:8090b77f0791560b3c01263f6222006fe4c1d1d526539344afc4ecd9bd3e56f2"},
|
||||
{file = "protobuf-3.15.5-py2.py3-none-any.whl", hash = "sha256:dbb98adb4281684eb54ce1f003b574bbc5768b9f614d7faa2c56f30e18519ec7"},
|
||||
{file = "protobuf-3.15.5.tar.gz", hash = "sha256:be8a929c6178bb6cbe9e2c858be62fa08966a39ae758a8493a88f0ed1efb6097"},
|
||||
]
|
||||
py = [
|
||||
{file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
|
||||
|
|
@ -1958,8 +1980,8 @@ pygments = [
|
|||
{file = "Pygments-2.8.0.tar.gz", hash = "sha256:37a13ba168a02ac54cc5891a42b1caec333e59b66addb7fa633ea8a6d73445c0"},
|
||||
]
|
||||
pylint = [
|
||||
{file = "pylint-2.7.1-py3-none-any.whl", hash = "sha256:a251b238db462b71d25948f940568bb5b3ae0e37dbaa05e10523f54f83e6cc7e"},
|
||||
{file = "pylint-2.7.1.tar.gz", hash = "sha256:81ce108f6342421169ea039ff1f528208c99d2e5a9c4ca95cfc5291be6dfd982"},
|
||||
{file = "pylint-2.7.2-py3-none-any.whl", hash = "sha256:d09b0b07ba06bcdff463958f53f23df25e740ecd81895f7d2699ec04bbd8dc3b"},
|
||||
{file = "pylint-2.7.2.tar.gz", hash = "sha256:0e21d3b80b96740909d77206d741aa3ce0b06b41be375d92e1f3244a274c1f8a"},
|
||||
]
|
||||
pymongo = [
|
||||
{file = "pymongo-3.11.3-cp27-cp27m-macosx_10_14_intel.whl", hash = "sha256:4d959e929cec805c2bf391418b1121590b4e7d5cb00af7b1ba521443d45a0918"},
|
||||
|
|
@ -2100,8 +2122,8 @@ pyrsistent = [
|
|||
{file = "pyrsistent-0.17.3.tar.gz", hash = "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"},
|
||||
]
|
||||
pytest = [
|
||||
{file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"},
|
||||
{file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"},
|
||||
{file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"},
|
||||
{file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"},
|
||||
]
|
||||
pytest-cov = [
|
||||
{file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"},
|
||||
|
|
@ -2275,8 +2297,8 @@ wcwidth = [
|
|||
{file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
|
||||
]
|
||||
websocket-client = [
|
||||
{file = "websocket_client-0.57.0-py2.py3-none-any.whl", hash = "sha256:0fc45c961324d79c781bab301359d5a1b00b13ad1b10415a4780229ef71a5549"},
|
||||
{file = "websocket_client-0.57.0.tar.gz", hash = "sha256:d735b91d6d1692a6a181f2a8c9e0238e5f6373356f561bb9dc4c7af36f452010"},
|
||||
{file = "websocket_client-0.58.0-py2.py3-none-any.whl", hash = "sha256:44b5df8f08c74c3d82d28100fdc81f4536809ce98a17f0757557813275fbb663"},
|
||||
{file = "websocket_client-0.58.0.tar.gz", hash = "sha256:63509b41d158ae5b7f67eb4ad20fecbb4eee99434e73e140354dc3ff8e09716f"},
|
||||
]
|
||||
wrapt = [
|
||||
{file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"},
|
||||
|
|
@ -2325,6 +2347,6 @@ yarl = [
|
|||
{file = "yarl-1.6.3.tar.gz", hash = "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10"},
|
||||
]
|
||||
zipp = [
|
||||
{file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"},
|
||||
{file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"},
|
||||
{file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"},
|
||||
{file = "zipp-3.4.1.tar.gz", hash = "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76"},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from maya import utils, cmds
|
|||
from avalon import api as avalon
|
||||
from avalon import pipeline
|
||||
from avalon.maya import suspended_refresh
|
||||
from avalon.maya.pipeline import IS_HEADLESS, _on_task_changed
|
||||
from avalon.maya.pipeline import IS_HEADLESS
|
||||
from avalon.tools import workfiles
|
||||
from pyblish import api as pyblish
|
||||
from pype.lib import any_outdated
|
||||
|
|
@ -45,9 +45,7 @@ def install():
|
|||
avalon.on("open", on_open)
|
||||
avalon.on("new", on_new)
|
||||
avalon.before("save", on_before_save)
|
||||
|
||||
log.info("Overriding existing event 'taskChanged'")
|
||||
override_event("taskChanged", on_task_changed)
|
||||
avalon.on("taskChanged", on_task_changed)
|
||||
|
||||
log.info("Setting default family states for loader..")
|
||||
avalon.data["familiesStateToggled"] = ["imagesequence"]
|
||||
|
|
@ -61,24 +59,6 @@ def uninstall():
|
|||
menu.uninstall()
|
||||
|
||||
|
||||
def override_event(event, callback):
|
||||
"""
|
||||
Override existing event callback
|
||||
Args:
|
||||
event (str): name of the event
|
||||
callback (function): callback to be triggered
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
"""
|
||||
|
||||
ref = weakref.WeakSet()
|
||||
ref.add(callback)
|
||||
|
||||
pipeline._registered_event_handlers[event] = ref
|
||||
|
||||
|
||||
def on_init(_):
|
||||
avalon.logger.info("Running callback on init..")
|
||||
|
||||
|
|
@ -215,7 +195,6 @@ def on_new(_):
|
|||
def on_task_changed(*args):
|
||||
"""Wrapped function of app initialize and maya's on task changed"""
|
||||
# Run
|
||||
_on_task_changed()
|
||||
with suspended_refresh():
|
||||
lib.set_context_settings()
|
||||
lib.update_content_on_context_change()
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ class GenerateUUIDsOnInvalidAction(pyblish.api.Action):
|
|||
nodes (list): all nodes to regenerate ids on
|
||||
"""
|
||||
|
||||
from pype.hosts.maya import lib
|
||||
from pype.hosts.maya.api import lib
|
||||
import avalon.io as io
|
||||
|
||||
asset = instance.data['asset']
|
||||
|
|
|
|||
|
|
@ -2127,15 +2127,9 @@ def bake_to_world_space(nodes,
|
|||
|
||||
|
||||
def load_capture_preset(path=None, data=None):
|
||||
import capture_gui
|
||||
import capture
|
||||
|
||||
if data:
|
||||
preset = data
|
||||
else:
|
||||
path = path
|
||||
preset = capture_gui.lib.load_json(path)
|
||||
print(preset)
|
||||
preset = data
|
||||
|
||||
options = dict()
|
||||
|
||||
|
|
@ -2177,29 +2171,27 @@ def load_capture_preset(path=None, data=None):
|
|||
|
||||
temp_options2 = {}
|
||||
id = 'Viewport Options'
|
||||
light_options = {
|
||||
0: "default",
|
||||
1: 'all',
|
||||
2: 'selected',
|
||||
3: 'flat',
|
||||
4: 'nolights'}
|
||||
for key in preset[id]:
|
||||
if key == 'high_quality':
|
||||
if preset[id][key] == True:
|
||||
temp_options2['multiSampleEnable'] = True
|
||||
temp_options2['multiSampleCount'] = 4
|
||||
temp_options2['textureMaxResolution'] = 1024
|
||||
if key == 'textureMaxResolution':
|
||||
if preset[id][key] > 0:
|
||||
temp_options2['textureMaxResolution'] = preset[id][key]
|
||||
temp_options2['enableTextureMaxRes'] = True
|
||||
temp_options2['textureMaxResMode'] = 1
|
||||
else:
|
||||
temp_options2['multiSampleEnable'] = False
|
||||
temp_options2['multiSampleCount'] = 4
|
||||
temp_options2['textureMaxResolution'] = 512
|
||||
temp_options2['enableTextureMaxRes'] = True
|
||||
temp_options2['textureMaxResolution'] = preset[id][key]
|
||||
temp_options2['enableTextureMaxRes'] = False
|
||||
temp_options2['textureMaxResMode'] = 0
|
||||
|
||||
if key == 'multiSample':
|
||||
if preset[id][key] > 0:
|
||||
temp_options2['multiSampleEnable'] = True
|
||||
temp_options2['multiSampleCount'] = preset[id][key]
|
||||
else:
|
||||
temp_options2['multiSampleEnable'] = False
|
||||
temp_options2['multiSampleCount'] = preset[id][key]
|
||||
|
||||
if key == 'ssaoEnable':
|
||||
if preset[id][key] == True:
|
||||
if preset[id][key] is True:
|
||||
temp_options2['ssaoEnable'] = True
|
||||
else:
|
||||
temp_options2['ssaoEnable'] = False
|
||||
|
|
@ -2211,18 +2203,17 @@ def load_capture_preset(path=None, data=None):
|
|||
if key == 'headsUpDisplay':
|
||||
temp_options['headsUpDisplay'] = True
|
||||
|
||||
if key == 'displayLights':
|
||||
temp_options[str(key)] = light_options[preset[id][key]]
|
||||
else:
|
||||
temp_options[str(key)] = preset[id][key]
|
||||
|
||||
for key in ['override_viewport_options',
|
||||
'high_quality',
|
||||
'alphaCut',
|
||||
'gpuCacheDisplayFilter']:
|
||||
temp_options.pop(key, None)
|
||||
|
||||
for key in ['ssaoEnable']:
|
||||
'gpuCacheDisplayFilter',
|
||||
'multiSample',
|
||||
'ssaoEnable',
|
||||
'textureMaxResolution'
|
||||
]:
|
||||
temp_options.pop(key, None)
|
||||
|
||||
options['viewport_options'] = temp_options
|
||||
|
|
@ -2686,7 +2677,7 @@ def update_content_on_context_change():
|
|||
|
||||
def show_message(title, msg):
|
||||
from avalon.vendor.Qt import QtWidgets
|
||||
from ...widgets import message_window
|
||||
from pype.widgets import message_window
|
||||
|
||||
# Find maya main window
|
||||
top_level_widgets = {w.objectName(): w for w in
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ class ExtractPlayblast(pype.api.Extractor):
|
|||
hosts = ["maya"]
|
||||
families = ["review"]
|
||||
optional = True
|
||||
capture_preset = {}
|
||||
|
||||
def process(self, instance):
|
||||
self.log.info("Extracting capture..")
|
||||
|
|
@ -43,15 +44,9 @@ class ExtractPlayblast(pype.api.Extractor):
|
|||
|
||||
# get cameras
|
||||
camera = instance.data['review_camera']
|
||||
capture_preset = (
|
||||
instance.context.data['project_settings']['maya']['capture']
|
||||
)
|
||||
|
||||
try:
|
||||
preset = lib.load_capture_preset(data=capture_preset)
|
||||
except Exception:
|
||||
preset = {}
|
||||
self.log.info('using viewport preset: {}'.format(preset))
|
||||
preset = lib.load_capture_preset(data=self.capture_preset)
|
||||
|
||||
|
||||
preset['camera'] = camera
|
||||
preset['format'] = "image"
|
||||
|
|
@ -101,6 +96,9 @@ class ExtractPlayblast(pype.api.Extractor):
|
|||
# Remove panel key since it's internal value to capture_gui
|
||||
preset.pop("panel", None)
|
||||
|
||||
|
||||
self.log.info('using viewport preset: {}'.format(preset))
|
||||
|
||||
path = capture.capture(**preset)
|
||||
playblast = self._fix_playblast_output_path(path)
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class ExtractThumbnail(pype.api.Extractor):
|
|||
|
||||
capture_preset = ""
|
||||
capture_preset = (
|
||||
instance.context.data["project_settings"]['maya']['capture']
|
||||
instance.context.data["project_settings"]['maya']['publish']['ExtractPlayblast']
|
||||
)
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ class LoadMov(api.Loader):
|
|||
containerise,
|
||||
viewer_update_and_undo_stop
|
||||
)
|
||||
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
repr_id = context["representation"]["_id"]
|
||||
|
|
|
|||
|
|
@ -73,5 +73,17 @@ class CreateImage(pype.api.Creator):
|
|||
groups.append(group)
|
||||
|
||||
for group in groups:
|
||||
long_names = []
|
||||
if group.long_name:
|
||||
for directory in group.long_name[::-1]:
|
||||
name = directory.replace(stub.PUBLISH_ICON, '').\
|
||||
replace(stub.LOADED_ICON, '')
|
||||
long_names.append(name)
|
||||
|
||||
self.data.update({"subset": "image" + group.name})
|
||||
self.data.update({"uuid": str(group.id)})
|
||||
self.data.update({"long_name": "_".join(long_names)})
|
||||
stub.imprint(group, self.data)
|
||||
# reusing existing group, need to rename afterwards
|
||||
if not create_group:
|
||||
stub.rename_layer(group.id, stub.PUBLISH_ICON + group.name)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
|||
stub = photoshop.stub()
|
||||
layers = stub.get_layers()
|
||||
layers_meta = stub.get_layers_metadata()
|
||||
instance_names = []
|
||||
for layer in layers:
|
||||
layer_data = stub.read(layer, layers_meta)
|
||||
|
||||
|
|
@ -41,14 +42,20 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
|||
# self.log.info("%s skipped, it was empty." % layer.Name)
|
||||
# continue
|
||||
|
||||
instance = context.create_instance(layer.name)
|
||||
instance = context.create_instance(layer_data["subset"])
|
||||
instance.append(layer)
|
||||
instance.data.update(layer_data)
|
||||
instance.data["families"] = self.families_mapping[
|
||||
layer_data["family"]
|
||||
]
|
||||
instance.data["publish"] = layer.visible
|
||||
instance_names.append(layer_data["subset"])
|
||||
|
||||
# Produce diagnostic message for any graphical
|
||||
# user interface interested in visualising it.
|
||||
self.log.info("Found: \"%s\" " % instance.data["name"])
|
||||
self.log.info("instance: {} ".format(instance.data))
|
||||
|
||||
if len(instance_names) != len(set(instance_names)):
|
||||
self.log.warning("Duplicate instances found. " +
|
||||
"Remove unwanted via SubsetManager")
|
||||
|
|
|
|||
|
|
@ -25,11 +25,15 @@ class ValidateNamingRepair(pyblish.api.Action):
|
|||
for instance in instances:
|
||||
self.log.info("validate_naming instance {}".format(instance))
|
||||
name = instance.data["name"].replace(" ", "_")
|
||||
name = name.replace(instance.data["family"], '')
|
||||
instance[0].Name = name
|
||||
data = stub.read(instance[0])
|
||||
data["subset"] = "image" + name
|
||||
stub.imprint(instance[0], data)
|
||||
|
||||
name = stub.PUBLISH_ICON + name
|
||||
stub.rename_layer(instance.data["uuid"], name)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
|
@ -46,8 +50,11 @@ class ValidateNaming(pyblish.api.InstancePlugin):
|
|||
actions = [ValidateNamingRepair]
|
||||
|
||||
def process(self, instance):
|
||||
msg = "Name \"{}\" is not allowed.".format(instance.data["name"])
|
||||
help_msg = ' Use Repair action (A) in Pyblish to fix it.'
|
||||
msg = "Name \"{}\" is not allowed.{}".format(instance.data["name"],
|
||||
help_msg)
|
||||
assert " " not in instance.data["name"], msg
|
||||
|
||||
msg = "Subset \"{}\" is not allowed.".format(instance.data["subset"])
|
||||
msg = "Subset \"{}\" is not allowed.{}".format(instance.data["subset"],
|
||||
help_msg)
|
||||
assert " " not in instance.data["subset"], msg
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
import pyblish.api
|
||||
import pype.api
|
||||
|
||||
|
||||
class ValidateSubsetUniqueness(pyblish.api.ContextPlugin):
|
||||
"""
|
||||
Validate that all subset's names are unique.
|
||||
"""
|
||||
|
||||
label = "Validate Subset Uniqueness"
|
||||
hosts = ["photoshop"]
|
||||
order = pype.api.ValidateContentsOrder
|
||||
families = ["image"]
|
||||
|
||||
def process(self, context):
|
||||
subset_names = []
|
||||
|
||||
for instance in context:
|
||||
if instance.data.get('publish'):
|
||||
subset_names.append(instance.data.get('subset'))
|
||||
|
||||
msg = (
|
||||
"Instance subset names are not unique. " +
|
||||
"Remove duplicates via SubsetManager."
|
||||
)
|
||||
assert len(subset_names) == len(set(subset_names)), msg
|
||||
|
|
@ -2,7 +2,10 @@ import os
|
|||
import shutil
|
||||
|
||||
from pype.hosts import tvpaint
|
||||
from pype.lib import PreLaunchHook
|
||||
from pype.lib import (
|
||||
PreLaunchHook,
|
||||
get_pype_execute_args
|
||||
)
|
||||
|
||||
import avalon
|
||||
|
||||
|
|
@ -20,18 +23,16 @@ class TvpaintPrelaunchHook(PreLaunchHook):
|
|||
|
||||
def execute(self):
|
||||
# Pop tvpaint executable
|
||||
tvpaint_executable = self.launch_context.launch_args.pop(0)
|
||||
executable_path = self.launch_context.launch_args.pop(0)
|
||||
|
||||
# Pop rest of launch arguments - There should not be other arguments!
|
||||
remainders = []
|
||||
while self.launch_context.launch_args:
|
||||
remainders.append(self.launch_context.launch_args.pop(0))
|
||||
|
||||
new_launch_args = [
|
||||
self.main_executable(),
|
||||
self.launch_script_path(),
|
||||
tvpaint_executable
|
||||
]
|
||||
new_launch_args = get_pype_execute_args(
|
||||
"run", self.launch_script_path(), executable_path
|
||||
)
|
||||
|
||||
# Add workfile to launch arguments
|
||||
workfile_path = self.workfile_path()
|
||||
|
|
@ -56,11 +57,6 @@ class TvpaintPrelaunchHook(PreLaunchHook):
|
|||
).format(str(remainders)))
|
||||
self.launch_context.launch_args.extend(remainders)
|
||||
|
||||
def main_executable(self):
|
||||
"""Should lead to python executable."""
|
||||
# TODO change in Pype 3
|
||||
return os.path.normpath(os.environ["PYPE_PYTHON_EXE"])
|
||||
|
||||
def launch_script_path(self):
|
||||
avalon_dir = os.path.dirname(os.path.abspath(avalon.__file__))
|
||||
script_path = os.path.join(
|
||||
|
|
|
|||
87
pype/lib/pype_info.py
Normal file
87
pype/lib/pype_info.py
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import os
|
||||
import json
|
||||
import datetime
|
||||
import platform
|
||||
import getpass
|
||||
import socket
|
||||
|
||||
import pype.version
|
||||
from pype.settings.lib import get_local_settings
|
||||
from .execute import get_pype_execute_args
|
||||
from .local_settings import get_local_site_id
|
||||
|
||||
|
||||
def get_pype_version():
|
||||
"""Version of pype that is currently used."""
|
||||
return pype.version.__version__
|
||||
|
||||
|
||||
def get_pype_info():
|
||||
"""Information about currently used Pype process."""
|
||||
executable_args = get_pype_execute_args()
|
||||
if len(executable_args) == 1:
|
||||
version_type = "build"
|
||||
else:
|
||||
version_type = "code"
|
||||
|
||||
return {
|
||||
"version": get_pype_version(),
|
||||
"version_type": version_type,
|
||||
"executable": executable_args[-1],
|
||||
"pype_root": os.environ["PYPE_ROOT"],
|
||||
"mongo_url": os.environ["PYPE_MONGO"]
|
||||
}
|
||||
|
||||
|
||||
def get_workstation_info():
|
||||
"""Basic information about workstation."""
|
||||
host_name = socket.gethostname()
|
||||
try:
|
||||
host_ip = socket.gethostbyname(host_name)
|
||||
except socket.gaierror:
|
||||
host_ip = "127.0.0.1"
|
||||
|
||||
return {
|
||||
"hostname": host_name,
|
||||
"hostip": host_ip,
|
||||
"username": getpass.getuser(),
|
||||
"system_name": platform.system(),
|
||||
"local_id": get_local_site_id()
|
||||
}
|
||||
|
||||
|
||||
def get_all_current_info():
|
||||
"""All information about current process in one dictionary."""
|
||||
return {
|
||||
"pype": get_pype_info(),
|
||||
"workstation": get_workstation_info(),
|
||||
"env": os.environ.copy(),
|
||||
"local_settings": get_local_settings()
|
||||
}
|
||||
|
||||
|
||||
def extract_pype_info_to_file(dirpath):
|
||||
"""Extract all current info to a file.
|
||||
|
||||
It is possible to define onpy directory path. Filename is concatenated with
|
||||
pype version, workstation site id and timestamp.
|
||||
|
||||
Args:
|
||||
dirpath (str): Path to directory where file will be stored.
|
||||
|
||||
Returns:
|
||||
filepath (str): Full path to file where data were extracted.
|
||||
"""
|
||||
filename = "{}_{}_{}.json".format(
|
||||
get_pype_version(),
|
||||
get_local_site_id(),
|
||||
datetime.datetime.now().strftime("%y%m%d%H%M%S")
|
||||
)
|
||||
filepath = os.path.join(dirpath, filename)
|
||||
data = get_all_current_info()
|
||||
if not os.path.exists(dirpath):
|
||||
os.makedirs(dirpath)
|
||||
|
||||
with open(filepath, "w") as file_stream:
|
||||
json.dump(data, file_stream, indent=4)
|
||||
return filepath
|
||||
|
|
@ -108,6 +108,7 @@ class ITrayModule:
|
|||
would do nothing.
|
||||
"""
|
||||
tray_initialized = False
|
||||
_tray_manager = None
|
||||
|
||||
@abstractmethod
|
||||
def tray_init(self):
|
||||
|
|
@ -138,6 +139,20 @@ class ITrayModule:
|
|||
"""
|
||||
pass
|
||||
|
||||
def show_tray_message(self, title, message, icon=None, msecs=None):
|
||||
"""Show tray message.
|
||||
|
||||
Args:
|
||||
title (str): Title of message.
|
||||
message (str): Content of message.
|
||||
icon (QSystemTrayIcon.MessageIcon): Message's icon. Default is
|
||||
Information icon, may differ by Qt version.
|
||||
msecs (int): Duration of message visibility in miliseconds.
|
||||
Default is 10000 msecs, may differ by Qt version.
|
||||
"""
|
||||
if self._tray_manager:
|
||||
self._tray_manager.show_tray_message(title, message, icon, msecs)
|
||||
|
||||
|
||||
class ITrayAction(ITrayModule):
|
||||
"""Implementation of Tray action.
|
||||
|
|
@ -638,8 +653,10 @@ class TrayModulesManager(ModulesManager):
|
|||
self.modules_by_id = {}
|
||||
self.modules_by_name = {}
|
||||
self._report = {}
|
||||
self.tray_manager = None
|
||||
|
||||
def initialize(self, tray_menu):
|
||||
def initialize(self, tray_manager, tray_menu):
|
||||
self.tray_manager = tray_manager
|
||||
self.initialize_modules()
|
||||
self.tray_init()
|
||||
self.connect_modules()
|
||||
|
|
@ -658,6 +675,7 @@ class TrayModulesManager(ModulesManager):
|
|||
prev_start_time = time_start
|
||||
for module in self.get_enabled_tray_modules():
|
||||
try:
|
||||
module._tray_manager = self.tray_manager
|
||||
module.tray_init()
|
||||
module.tray_initialized = True
|
||||
except Exception:
|
||||
|
|
|
|||
|
|
@ -54,6 +54,9 @@ class Photoshop(WebSocketRoute):
|
|||
async def projectmanager_route(self):
|
||||
self._tool_route("projectmanager")
|
||||
|
||||
async def subsetmanager_route(self):
|
||||
self._tool_route("subsetmanager")
|
||||
|
||||
def _tool_route(self, tool_name):
|
||||
"""The address accessed when clicking on the buttons."""
|
||||
partial_method = functools.partial(photoshop.show, tool_name)
|
||||
|
|
|
|||
|
|
@ -4,16 +4,37 @@ from pype.modules.websocket_server import WebSocketServer
|
|||
Used anywhere solution is calling client methods.
|
||||
"""
|
||||
import json
|
||||
from collections import namedtuple
|
||||
import attr
|
||||
|
||||
|
||||
class PhotoshopServerStub():
|
||||
@attr.s
|
||||
class PSItem(object):
|
||||
"""
|
||||
Object denoting layer or group item in PS. Each item is created in
|
||||
PS by any Loader, but contains same fields, which are being used
|
||||
in later processing.
|
||||
"""
|
||||
# metadata
|
||||
id = attr.ib() # id created by AE, could be used for querying
|
||||
name = attr.ib() # name of item
|
||||
group = attr.ib(default=None) # item type (footage, folder, comp)
|
||||
parents = attr.ib(factory=list)
|
||||
visible = attr.ib(default=True)
|
||||
type = attr.ib(default=None)
|
||||
# all imported elements, single for
|
||||
members = attr.ib(factory=list)
|
||||
long_name = attr.ib(default=None)
|
||||
|
||||
|
||||
class PhotoshopServerStub:
|
||||
"""
|
||||
Stub for calling function on client (Photoshop js) side.
|
||||
Expects that client is already connected (started when avalon menu
|
||||
is opened).
|
||||
'self.websocketserver.call' is used as async wrapper
|
||||
"""
|
||||
PUBLISH_ICON = '\u2117 '
|
||||
LOADED_ICON = '\u25bc'
|
||||
|
||||
def __init__(self):
|
||||
self.websocketserver = WebSocketServer.get_instance()
|
||||
|
|
@ -34,7 +55,7 @@ class PhotoshopServerStub():
|
|||
"""
|
||||
Parses layer metadata from Headline field of active document
|
||||
Args:
|
||||
layer: <namedTuple Layer("id":XX, "name":"YYY")
|
||||
layer: (PSItem)
|
||||
layers_meta: full list from Headline (for performance in loops)
|
||||
Returns:
|
||||
"""
|
||||
|
|
@ -46,10 +67,33 @@ class PhotoshopServerStub():
|
|||
def imprint(self, layer, data, all_layers=None, layers_meta=None):
|
||||
"""
|
||||
Save layer metadata to Headline field of active document
|
||||
|
||||
Stores metadata in format:
|
||||
[{
|
||||
"active":true,
|
||||
"subset":"imageBG",
|
||||
"family":"image",
|
||||
"id":"pyblish.avalon.instance",
|
||||
"asset":"Town",
|
||||
"uuid": "8"
|
||||
}] - for created instances
|
||||
OR
|
||||
[{
|
||||
"schema": "avalon-core:container-2.0",
|
||||
"id": "pyblish.avalon.instance",
|
||||
"name": "imageMG",
|
||||
"namespace": "Jungle_imageMG_001",
|
||||
"loader": "ImageLoader",
|
||||
"representation": "5fbfc0ee30a946093c6ff18a",
|
||||
"members": [
|
||||
"40"
|
||||
]
|
||||
}] - for loaded instances
|
||||
|
||||
Args:
|
||||
layer (namedtuple): Layer("id": XXX, "name":'YYY')
|
||||
layer (PSItem):
|
||||
data(string): json representation for single layer
|
||||
all_layers (list of namedtuples): for performance, could be
|
||||
all_layers (list of PSItem): for performance, could be
|
||||
injected for usage in loop, if not, single call will be
|
||||
triggered
|
||||
layers_meta(string): json representation from Headline
|
||||
|
|
@ -59,6 +103,7 @@ class PhotoshopServerStub():
|
|||
"""
|
||||
if not layers_meta:
|
||||
layers_meta = self.get_layers_metadata()
|
||||
|
||||
# json.dumps writes integer values in a dictionary to string, so
|
||||
# anticipating it here.
|
||||
if str(layer.id) in layers_meta and layers_meta[str(layer.id)]:
|
||||
|
|
@ -73,11 +118,11 @@ class PhotoshopServerStub():
|
|||
if not all_layers:
|
||||
all_layers = self.get_layers()
|
||||
layer_ids = [layer.id for layer in all_layers]
|
||||
cleaned_data = {}
|
||||
cleaned_data = []
|
||||
|
||||
for id in layers_meta:
|
||||
if int(id) in layer_ids:
|
||||
cleaned_data[id] = layers_meta[id]
|
||||
cleaned_data.append(layers_meta[id])
|
||||
|
||||
payload = json.dumps(cleaned_data, indent=4)
|
||||
|
||||
|
|
@ -89,7 +134,7 @@ class PhotoshopServerStub():
|
|||
"""
|
||||
Returns JSON document with all(?) layers in active document.
|
||||
|
||||
Returns: <list of namedtuples>
|
||||
Returns: <list of PSItem>
|
||||
Format of tuple: { 'id':'123',
|
||||
'name': 'My Layer 1',
|
||||
'type': 'GUIDE'|'FG'|'BG'|'OBJ'
|
||||
|
|
@ -100,12 +145,26 @@ class PhotoshopServerStub():
|
|||
|
||||
return self._to_records(res)
|
||||
|
||||
def get_layer(self, layer_id):
|
||||
"""
|
||||
Returns PSItem for specific 'layer_id' or None if not found
|
||||
Args:
|
||||
layer_id (string): unique layer id, stored in 'uuid' field
|
||||
|
||||
Returns:
|
||||
(PSItem) or None
|
||||
"""
|
||||
layers = self.get_layers()
|
||||
for layer in layers:
|
||||
if str(layer.id) == str(layer_id):
|
||||
return layer
|
||||
|
||||
def get_layers_in_layers(self, layers):
|
||||
"""
|
||||
Return all layers that belong to layers (might be groups).
|
||||
Args:
|
||||
layers <list of namedTuples>:
|
||||
Returns: <list of namedTuples>
|
||||
layers <list of PSItem>:
|
||||
Returns: <list of PSItem>
|
||||
"""
|
||||
all_layers = self.get_layers()
|
||||
ret = []
|
||||
|
|
@ -123,28 +182,30 @@ class PhotoshopServerStub():
|
|||
def create_group(self, name):
|
||||
"""
|
||||
Create new group (eg. LayerSet)
|
||||
Returns: <namedTuple Layer("id":XX, "name":"YYY")>
|
||||
Returns: <PSItem>
|
||||
"""
|
||||
enhanced_name = self.PUBLISH_ICON + name
|
||||
ret = self.websocketserver.call(self.client.call
|
||||
('Photoshop.create_group',
|
||||
name=name))
|
||||
name=enhanced_name))
|
||||
# create group on PS is asynchronous, returns only id
|
||||
layer = {"id": ret, "name": name, "group": True}
|
||||
return namedtuple('Layer', layer.keys())(*layer.values())
|
||||
return PSItem(id=ret, name=name, group=True)
|
||||
|
||||
def group_selected_layers(self, name):
|
||||
"""
|
||||
Group selected layers into new LayerSet (eg. group)
|
||||
Returns: (Layer)
|
||||
"""
|
||||
enhanced_name = self.PUBLISH_ICON + name
|
||||
res = self.websocketserver.call(self.client.call
|
||||
('Photoshop.group_selected_layers',
|
||||
name=name)
|
||||
name=enhanced_name)
|
||||
)
|
||||
res = self._to_records(res)
|
||||
|
||||
if res:
|
||||
return res.pop()
|
||||
rec = res.pop()
|
||||
rec.name = rec.name.replace(self.PUBLISH_ICON, '')
|
||||
return rec
|
||||
raise ValueError("No group record returned")
|
||||
|
||||
def get_selected_layers(self):
|
||||
|
|
@ -163,11 +224,10 @@ class PhotoshopServerStub():
|
|||
layers: <list of Layer('id':XX, 'name':"YYY")>
|
||||
Returns: None
|
||||
"""
|
||||
layer_ids = [layer.id for layer in layers]
|
||||
|
||||
layers_id = [str(lay.id) for lay in layers]
|
||||
self.websocketserver.call(self.client.call
|
||||
('Photoshop.get_layers',
|
||||
layers=layer_ids)
|
||||
('Photoshop.select_layers',
|
||||
layers=json.dumps(layers_id))
|
||||
)
|
||||
|
||||
def get_active_document_full_name(self):
|
||||
|
|
@ -238,7 +298,14 @@ class PhotoshopServerStub():
|
|||
"""
|
||||
Reads layers metadata from Headline from active document in PS.
|
||||
(Headline accessible by File > File Info)
|
||||
Returns(string): - json documents
|
||||
|
||||
Returns:
|
||||
(string): - json documents
|
||||
example:
|
||||
{"8":{"active":true,"subset":"imageBG",
|
||||
"family":"image","id":"pyblish.avalon.instance",
|
||||
"asset":"Town"}}
|
||||
8 is layer(group) id - used for deletion, update etc.
|
||||
"""
|
||||
layers_data = {}
|
||||
res = self.websocketserver.call(self.client.call('Photoshop.read'))
|
||||
|
|
@ -246,6 +313,23 @@ class PhotoshopServerStub():
|
|||
layers_data = json.loads(res)
|
||||
except json.decoder.JSONDecodeError:
|
||||
pass
|
||||
# format of metadata changed from {} to [] because of standardization
|
||||
# keep current implementation logic as its working
|
||||
if not isinstance(layers_data, dict):
|
||||
temp_layers_meta = {}
|
||||
for layer_meta in layers_data:
|
||||
layer_id = layer_meta.get("uuid") or \
|
||||
(layer_meta.get("members")[0])
|
||||
temp_layers_meta[layer_id] = layer_meta
|
||||
layers_data = temp_layers_meta
|
||||
else:
|
||||
# legacy version of metadata
|
||||
for layer_id, layer_meta in layers_data.items():
|
||||
if layer_meta.get("schema") != "avalon-core:container-2.0":
|
||||
layer_meta["uuid"] = str(layer_id)
|
||||
else:
|
||||
layer_meta["members"] = [str(layer_id)]
|
||||
|
||||
return layers_data
|
||||
|
||||
def import_smart_object(self, path, layer_name):
|
||||
|
|
@ -257,11 +341,14 @@ class PhotoshopServerStub():
|
|||
layer_name (str): Unique layer name to differentiate how many times
|
||||
same smart object was loaded
|
||||
"""
|
||||
enhanced_name = self.LOADED_ICON + layer_name
|
||||
res = self.websocketserver.call(self.client.call
|
||||
('Photoshop.import_smart_object',
|
||||
path=path, name=layer_name))
|
||||
|
||||
return self._to_records(res).pop()
|
||||
path=path, name=enhanced_name))
|
||||
rec = self._to_records(res).pop()
|
||||
if rec:
|
||||
rec.name = rec.name.replace(self.LOADED_ICON, '')
|
||||
return rec
|
||||
|
||||
def replace_smart_object(self, layer, path, layer_name):
|
||||
"""
|
||||
|
|
@ -270,13 +357,14 @@ class PhotoshopServerStub():
|
|||
same smart object was loaded
|
||||
|
||||
Args:
|
||||
layer (namedTuple): Layer("id":XX, "name":"YY"..).
|
||||
layer (PSItem):
|
||||
path (str): File to import.
|
||||
"""
|
||||
enhanced_name = self.LOADED_ICON + layer_name
|
||||
self.websocketserver.call(self.client.call
|
||||
('Photoshop.replace_smart_object',
|
||||
layer_id=layer.id,
|
||||
path=path, name=layer_name))
|
||||
path=path, name=enhanced_name))
|
||||
|
||||
def delete_layer(self, layer_id):
|
||||
"""
|
||||
|
|
@ -288,24 +376,62 @@ class PhotoshopServerStub():
|
|||
('Photoshop.delete_layer',
|
||||
layer_id=layer_id))
|
||||
|
||||
def rename_layer(self, layer_id, name):
|
||||
"""
|
||||
Renames specific layer by it's id.
|
||||
Args:
|
||||
layer_id (int): id of layer to delete
|
||||
name (str): new name
|
||||
"""
|
||||
self.websocketserver.call(self.client.call
|
||||
('Photoshop.rename_layer',
|
||||
layer_id=layer_id,
|
||||
name=name))
|
||||
|
||||
def remove_instance(self, instance_id):
|
||||
cleaned_data = {}
|
||||
|
||||
for key, instance in self.get_layers_metadata().items():
|
||||
if key != instance_id:
|
||||
cleaned_data[key] = instance
|
||||
|
||||
payload = json.dumps(cleaned_data, indent=4)
|
||||
|
||||
self.websocketserver.call(self.client.call
|
||||
('Photoshop.imprint', payload=payload)
|
||||
)
|
||||
|
||||
def close(self):
|
||||
self.client.close()
|
||||
|
||||
def _to_records(self, res):
|
||||
"""
|
||||
Converts string json representation into list of named tuples for
|
||||
Converts string json representation into list of PSItem for
|
||||
dot notation access to work.
|
||||
Returns: <list of named tuples>
|
||||
res(string): - json representation
|
||||
Args:
|
||||
res (string): valid json
|
||||
Returns:
|
||||
<list of PSItem>
|
||||
"""
|
||||
try:
|
||||
layers_data = json.loads(res)
|
||||
except json.decoder.JSONDecodeError:
|
||||
raise ValueError("Received broken JSON {}".format(res))
|
||||
ret = []
|
||||
# convert to namedtuple to use dot donation
|
||||
if isinstance(layers_data, dict): # TODO refactore
|
||||
|
||||
# convert to AEItem to use dot donation
|
||||
if isinstance(layers_data, dict):
|
||||
layers_data = [layers_data]
|
||||
for d in layers_data:
|
||||
ret.append(namedtuple('Layer', d.keys())(*d.values()))
|
||||
# currently implemented and expected fields
|
||||
item = PSItem(d.get('id'),
|
||||
d.get('name'),
|
||||
d.get('group'),
|
||||
d.get('parents'),
|
||||
d.get('visible'),
|
||||
d.get('type'),
|
||||
d.get('members'),
|
||||
d.get('long_name'))
|
||||
|
||||
ret.append(item)
|
||||
return ret
|
||||
|
|
|
|||
|
|
@ -113,15 +113,6 @@ class PypeCommands:
|
|||
def texture_copy(self, project, asset, path):
|
||||
pass
|
||||
|
||||
def run_pype_tests(self, keyword, id):
|
||||
pass
|
||||
|
||||
def make_docs(self):
|
||||
pass
|
||||
|
||||
def pype_setup_coverage(self):
|
||||
pass
|
||||
|
||||
def run_application(self, app, project, asset, task, tools, arguments):
|
||||
pass
|
||||
|
||||
|
|
@ -145,7 +136,7 @@ class PypeCommands:
|
|||
bs.data_dir = out_path.parent
|
||||
|
||||
print(f">>> Creating zip in {bs.data_dir} ...")
|
||||
repo_file = bs.install_live_repos()
|
||||
repo_file = bs.create_version_from_live_code()
|
||||
if not repo_file:
|
||||
print("!!! Error while creating zip file.")
|
||||
exit(1)
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
<svg version="1.1" id="loader-1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="312px" height="312px" viewBox="0 0 40 40" xml:space="preserve">
|
||||
<path opacity="0.2" fill="#ffa500" d="M20.201,5.169c-8.254,0-14.946,6.692-14.946,14.946c0,8.255,6.692,14.946,14.946,14.946
|
||||
s14.946-6.691,14.946-14.946C35.146,11.861,28.455,5.169,20.201,5.169z M20.201,31.749c-6.425,0-11.634-5.208-11.634-11.634
|
||||
c0-6.425,5.209-11.634,11.634-11.634c6.425,0,11.633,5.209,11.633,11.634C31.834,26.541,26.626,31.749,20.201,31.749z"/>
|
||||
<path fill="#ffa500" d="M26.013,10.047l1.654-2.866c-2.198-1.272-4.743-2.012-7.466-2.012h0v3.312h0
|
||||
C22.32,8.481,24.301,9.057,26.013,10.047z">
|
||||
<animateTransform attributeType="xml"
|
||||
attributeName="transform"
|
||||
type="rotate"
|
||||
from="00 20.2 20.1"
|
||||
to="360 20.2 20.1"
|
||||
dur="0.5s"
|
||||
repeatCount="indefinite"/>
|
||||
</path>
|
||||
<text x="3" y="23" fill="#ffa500" font-style="bold" font-size="7px" font-family="sans-serif">Working...</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1 KiB |
|
|
@ -1,111 +1,11 @@
|
|||
{
|
||||
"capture": {
|
||||
"Codec": {
|
||||
"compression": "jpg",
|
||||
"format": "image",
|
||||
"quality": 95
|
||||
},
|
||||
"Display Options": {
|
||||
"background": [
|
||||
0.7,
|
||||
0.7,
|
||||
0.7
|
||||
],
|
||||
"backgroundBottom": [
|
||||
0.7,
|
||||
0.7,
|
||||
0.7
|
||||
],
|
||||
"backgroundTop": [
|
||||
0.7,
|
||||
0.7,
|
||||
0.7
|
||||
],
|
||||
"override_display": true
|
||||
},
|
||||
"Generic": {
|
||||
"isolate_view": true,
|
||||
"off_screen": true
|
||||
},
|
||||
"IO": {
|
||||
"name": "",
|
||||
"open_finished": true,
|
||||
"raw_frame_numbers": true,
|
||||
"recent_playblasts": [],
|
||||
"save_file": true
|
||||
},
|
||||
"PanZoom": {
|
||||
"pan_zoom": true
|
||||
},
|
||||
"Renderer": {
|
||||
"rendererName": "vp2Renderer"
|
||||
},
|
||||
"Resolution": {
|
||||
"width": 1080,
|
||||
"height": 1920,
|
||||
"percent": 1.0,
|
||||
"mode": "Custom"
|
||||
},
|
||||
"Time Range": {
|
||||
"start_frame": 0,
|
||||
"end_frame": 0,
|
||||
"frame": "",
|
||||
"time": "Time Slider"
|
||||
},
|
||||
"Viewport Options": {
|
||||
"cameras": false,
|
||||
"clipGhosts": false,
|
||||
"controlVertices": false,
|
||||
"deformers": false,
|
||||
"dimensions": false,
|
||||
"displayLights": 0,
|
||||
"dynamicConstraints": false,
|
||||
"dynamics": false,
|
||||
"fluids": false,
|
||||
"follicles": false,
|
||||
"gpuCacheDisplayFilter": false,
|
||||
"greasePencils": false,
|
||||
"grid": false,
|
||||
"hairSystems": true,
|
||||
"handles": false,
|
||||
"high_quality": true,
|
||||
"hud": false,
|
||||
"hulls": false,
|
||||
"ikHandles": false,
|
||||
"imagePlane": true,
|
||||
"joints": false,
|
||||
"lights": false,
|
||||
"locators": false,
|
||||
"manipulators": false,
|
||||
"motionTrails": false,
|
||||
"nCloths": false,
|
||||
"nParticles": false,
|
||||
"nRigids": false,
|
||||
"nurbsCurves": false,
|
||||
"nurbsSurfaces": false,
|
||||
"override_viewport_options": true,
|
||||
"particleInstancers": false,
|
||||
"pivots": false,
|
||||
"planes": false,
|
||||
"pluginShapes": false,
|
||||
"polymeshes": true,
|
||||
"shadows": true,
|
||||
"strokes": false,
|
||||
"subdivSurfaces": false,
|
||||
"textures": false,
|
||||
"twoSidedLighting": true
|
||||
},
|
||||
"Camera Options": {
|
||||
"displayGateMask": false,
|
||||
"displayResolution": false,
|
||||
"displayFilmGate": false,
|
||||
"displayFieldChart": false,
|
||||
"displaySafeAction": false,
|
||||
"displaySafeTitle": false,
|
||||
"displayFilmPivot": false,
|
||||
"displayFilmOrigin": false,
|
||||
"overscan": 1.0
|
||||
}
|
||||
"ext_mapping": {
|
||||
"model": "ma",
|
||||
"mayaAscii": "ma",
|
||||
"camera": "ma",
|
||||
"rig": "ma",
|
||||
"workfile": "ma",
|
||||
"yetiRig": "ma"
|
||||
},
|
||||
"create": {
|
||||
"CreateAnimation": {
|
||||
|
|
@ -299,6 +199,10 @@
|
|||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateMeshNormalsUnlocked": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateMeshUVSetMap1": {
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
|
|
@ -336,7 +240,7 @@
|
|||
"optional": true
|
||||
},
|
||||
"ValidateTransformZero": {
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateCameraAttributes": {
|
||||
|
|
@ -351,6 +255,105 @@
|
|||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ExtractPlayblast": {
|
||||
"capture_preset": {
|
||||
"Codec": {
|
||||
"compression": "jpg",
|
||||
"format": "image",
|
||||
"quality": 95
|
||||
},
|
||||
"Display Options": {
|
||||
"background": [
|
||||
0.7,
|
||||
0.7,
|
||||
0.7
|
||||
],
|
||||
"backgroundBottom": [
|
||||
0.7,
|
||||
0.7,
|
||||
0.7
|
||||
],
|
||||
"backgroundTop": [
|
||||
0.7,
|
||||
0.7,
|
||||
0.7
|
||||
],
|
||||
"override_display": true
|
||||
},
|
||||
"Generic": {
|
||||
"isolate_view": true,
|
||||
"off_screen": true
|
||||
},
|
||||
"PanZoom": {
|
||||
"pan_zoom": true
|
||||
},
|
||||
"Renderer": {
|
||||
"rendererName": "vp2Renderer"
|
||||
},
|
||||
"Resolution": {
|
||||
"width": 1080,
|
||||
"height": 1920,
|
||||
"percent": 1.0,
|
||||
"mode": "Custom"
|
||||
},
|
||||
"Viewport Options": {
|
||||
"override_viewport_options": true,
|
||||
"displayLights": "0",
|
||||
"textureMaxResolution": 1024,
|
||||
"multiSample": 4,
|
||||
"shadows": true,
|
||||
"textures": true,
|
||||
"twoSidedLighting": true,
|
||||
"ssaoEnable": true,
|
||||
"cameras": false,
|
||||
"clipGhosts": false,
|
||||
"controlVertices": false,
|
||||
"deformers": false,
|
||||
"dimensions": false,
|
||||
"dynamicConstraints": false,
|
||||
"dynamics": false,
|
||||
"fluids": false,
|
||||
"follicles": false,
|
||||
"gpuCacheDisplayFilter": false,
|
||||
"greasePencils": false,
|
||||
"grid": false,
|
||||
"hairSystems": true,
|
||||
"handles": false,
|
||||
"hud": false,
|
||||
"hulls": false,
|
||||
"ikHandles": false,
|
||||
"imagePlane": true,
|
||||
"joints": false,
|
||||
"lights": false,
|
||||
"locators": false,
|
||||
"manipulators": false,
|
||||
"motionTrails": false,
|
||||
"nCloths": false,
|
||||
"nParticles": false,
|
||||
"nRigids": false,
|
||||
"nurbsCurves": false,
|
||||
"nurbsSurfaces": false,
|
||||
"particleInstancers": false,
|
||||
"pivots": false,
|
||||
"planes": false,
|
||||
"pluginShapes": false,
|
||||
"polymeshes": true,
|
||||
"strokes": false,
|
||||
"subdivSurfaces": false
|
||||
},
|
||||
"Camera Options": {
|
||||
"displayGateMask": false,
|
||||
"displayResolution": false,
|
||||
"displayFilmGate": false,
|
||||
"displayFieldChart": false,
|
||||
"displaySafeAction": false,
|
||||
"displaySafeTitle": false,
|
||||
"displayFilmPivot": false,
|
||||
"displayFilmOrigin": false,
|
||||
"overscan": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
"ExtractCameraAlembic": {
|
||||
"enabled": true,
|
||||
"optional": true,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,30 @@
|
|||
"PreCollectNukeInstances": {
|
||||
"sync_workfile_version": true
|
||||
},
|
||||
"ValidateKnobs": {
|
||||
"enabled": false,
|
||||
"knobs": {
|
||||
"render": {
|
||||
"review": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ValidateOutputResolution": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateGizmo": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateScript": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateNukeWriteBoundingBox": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ExtractThumbnail": {
|
||||
"enabled": true,
|
||||
"nodes": {
|
||||
|
|
@ -38,14 +62,6 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"ValidateKnobs": {
|
||||
"enabled": false,
|
||||
"knobs": {
|
||||
"render": {
|
||||
"review": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"ExtractReviewDataLut": {
|
||||
"enabled": false
|
||||
},
|
||||
|
|
@ -61,22 +77,25 @@
|
|||
"deadline_pool": "",
|
||||
"deadline_pool_secondary": "",
|
||||
"deadline_chunk_size": 1
|
||||
},
|
||||
"ValidateOutputResolution": {
|
||||
}
|
||||
},
|
||||
"load": {
|
||||
"LoadImage": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
"representations": []
|
||||
},
|
||||
"ValidateGizmo": {
|
||||
"LoadMov": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
"representations": []
|
||||
},
|
||||
"ValidateScript": {
|
||||
"LoadSequence": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ValidateNukeWriteBoundingBox": {
|
||||
"enabled": true,
|
||||
"optional": true
|
||||
"representations": [
|
||||
"png",
|
||||
"jpg",
|
||||
"exr",
|
||||
""
|
||||
]
|
||||
}
|
||||
},
|
||||
"workfile_build": {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,4 @@
|
|||
{
|
||||
"publish": {
|
||||
"ExtractThumbnailSP": {
|
||||
"ffmpeg_args": {
|
||||
"input": [
|
||||
"gamma 2.2"
|
||||
],
|
||||
"output": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"create": {
|
||||
"create_workfile": {
|
||||
"name": "workfile",
|
||||
|
|
@ -121,5 +111,15 @@
|
|||
"create_image": "Image",
|
||||
"create_matchmove": "Matchmove"
|
||||
}
|
||||
},
|
||||
"publish": {
|
||||
"ExtractThumbnailSP": {
|
||||
"ffmpeg_args": {
|
||||
"input": [
|
||||
"gamma 2.2"
|
||||
],
|
||||
"output": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,21 +17,11 @@
|
|||
"darwin": "{PYPE_ROOT}/vendor/bin/ffmpeg_exec/darwin/bin",
|
||||
"linux": ":{PYPE_ROOT}/vendor/bin/ffmpeg_exec/linux"
|
||||
},
|
||||
"PYPE_PYTHON_EXE": {
|
||||
"windows": "{VIRTUAL_ENV}/Scripts/python.exe",
|
||||
"linux": "{VIRTUAL_ENV}/Scripts/python",
|
||||
"darwin": "{VIRTUAL_ENV}/bin/python"
|
||||
},
|
||||
"PYPE_OCIO_CONFIG": "{STUDIO_SOFT}/OpenColorIO-Configs",
|
||||
"PYBLISH_GUI": "pyblish_pype",
|
||||
"QT_AUTO_SCREEN_SCALE_FACTOR": "1",
|
||||
"__environment_keys__": {
|
||||
"global": [
|
||||
"FFMPEG_PATH",
|
||||
"PYPE_PYTHON_EXE",
|
||||
"PYPE_OCIO_CONFIG",
|
||||
"PYBLISH_GUI",
|
||||
"QT_AUTO_SCREEN_SCALE_FACTOR"
|
||||
"PYPE_OCIO_CONFIG"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,13 @@
|
|||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_maya_capture"
|
||||
"type": "dict-modifiable",
|
||||
"key": "ext_mapping",
|
||||
"label": "Extension Mapping",
|
||||
"use_label_wrap": true,
|
||||
"object_type": {
|
||||
"type": "text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@
|
|||
"type": "schema",
|
||||
"name": "schema_nuke_publish",
|
||||
"template_data": []
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
"name": "schema_nuke_load",
|
||||
"template_data": []
|
||||
},
|
||||
{
|
||||
"type": "schema",
|
||||
|
|
|
|||
|
|
@ -1,581 +0,0 @@
|
|||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "capture",
|
||||
"label": "Maya Playblast settings",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "Codec",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "<b>Codec</b>"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "compression",
|
||||
"label": "Compression type"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "format",
|
||||
"label": "Data format"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "quality",
|
||||
"label": "Quality",
|
||||
"decimal": 0,
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
},
|
||||
|
||||
{
|
||||
"type": "splitter"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "Display Options",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "<b>Display Options</b>"
|
||||
},
|
||||
{
|
||||
"type": "list-strict",
|
||||
"key": "background",
|
||||
"label": "Background Color: ",
|
||||
"object_types": [
|
||||
{
|
||||
"label": "Red",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Green",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Blue",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "list-strict",
|
||||
"key": "backgroundBottom",
|
||||
"label": "Background Bottom: ",
|
||||
"object_types": [
|
||||
{
|
||||
"label": "Red",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Green",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Blue",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "list-strict",
|
||||
"key": "backgroundTop",
|
||||
"label": "Background Top: ",
|
||||
"object_types": [
|
||||
{
|
||||
"label": "Red",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Green",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Blue",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "override_display",
|
||||
"label": "Override display options"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "Generic",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "<b>Generic</b>"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "isolate_view",
|
||||
"label": " Isolate view"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "off_screen",
|
||||
"label": " Off Screen"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "IO",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "<b>IO</b>"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "name",
|
||||
"label": "Name"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "open_finished",
|
||||
"label": "Open finished"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "raw_frame_numbers",
|
||||
"label": "Raw frame numbers"
|
||||
},
|
||||
{
|
||||
"type": "list",
|
||||
"key": "recent_playblasts",
|
||||
"label": "Recent Playblasts",
|
||||
"object_type": "text"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "save_file",
|
||||
"label": "Save file"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "PanZoom",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "pan_zoom",
|
||||
"label": " Pan Zoom"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "Renderer",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "<b>Renderer</b>"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "rendererName",
|
||||
"label": " Renderer name"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "Resolution",
|
||||
"children": [
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "<b>Resolution</b>"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "width",
|
||||
"label": " Width",
|
||||
"decimal": 0,
|
||||
"minimum": 0,
|
||||
"maximum": 99999
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "height",
|
||||
"label": "Height",
|
||||
"decimal": 0,
|
||||
"minimum": 0,
|
||||
"maximum": 99999
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "percent",
|
||||
"label": "percent",
|
||||
"decimal": 1,
|
||||
"minimum": 0,
|
||||
"maximum": 200
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "mode",
|
||||
"label": "Mode"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "Time Range",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "<b>Time Range</b>"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "start_frame",
|
||||
"label": " Start frame",
|
||||
"decimal": 0,
|
||||
"minimum": 0,
|
||||
"maximum": 999999
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "end_frame",
|
||||
"label": "End frame",
|
||||
"decimal": 0,
|
||||
"minimum": 0,
|
||||
"maximum": 999999
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "frame",
|
||||
"label": "Frame"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "time",
|
||||
"label": "Time"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "Viewport Options",
|
||||
"label": "Viewport Options",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "cameras",
|
||||
"label": "cameras"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "clipGhosts",
|
||||
"label": "clipGhosts"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "controlVertices",
|
||||
"label": "controlVertices"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "deformers",
|
||||
"label": "deformers"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "dimensions",
|
||||
"label": "dimensions"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "displayLights",
|
||||
"label": "displayLights",
|
||||
"decimal": 0,
|
||||
"minimum": 0,
|
||||
"maximum": 10
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "dynamicConstraints",
|
||||
"label": "dynamicConstraints"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "dynamics",
|
||||
"label": "dynamics"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "fluids",
|
||||
"label": "fluids"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "follicles",
|
||||
"label": "follicles"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "gpuCacheDisplayFilter",
|
||||
"label": "gpuCacheDisplayFilter"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "greasePencils",
|
||||
"label": "greasePencils"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "grid",
|
||||
"label": "grid"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "hairSystems",
|
||||
"label": "hairSystems"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "handles",
|
||||
"label": "handles"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "high_quality",
|
||||
"label": "high_quality"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "hud",
|
||||
"label": "hud"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "hulls",
|
||||
"label": "hulls"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "ikHandles",
|
||||
"label": "ikHandles"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "imagePlane",
|
||||
"label": "imagePlane"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "joints",
|
||||
"label": "joints"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "lights",
|
||||
"label": "lights"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "locators",
|
||||
"label": "locators"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "manipulators",
|
||||
"label": "manipulators"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "motionTrails",
|
||||
"label": "motionTrails"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "nCloths",
|
||||
"label": "nCloths"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "nParticles",
|
||||
"label": "nParticles"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "nRigids",
|
||||
"label": "nRigids"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "nurbsCurves",
|
||||
"label": "nurbsCurves"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "nurbsSurfaces",
|
||||
"label": "nurbsSurfaces"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "override_viewport_options",
|
||||
"label": "override_viewport_options"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "particleInstancers",
|
||||
"label": "particleInstancers"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "pivots",
|
||||
"label": "pivots"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "planes",
|
||||
"label": "planes"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "pluginShapes",
|
||||
"label": "pluginShapes"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "polymeshes",
|
||||
"label": "polymeshes"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "shadows",
|
||||
"label": "shadows"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "strokes",
|
||||
"label": "strokes"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "subdivSurfaces",
|
||||
"label": "subdivSurfaces"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "textures",
|
||||
"label": "textures"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "twoSidedLighting",
|
||||
"label": "twoSidedLighting"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "Camera Options",
|
||||
"label": "Camera Options",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displayGateMask",
|
||||
"label": "displayGateMask"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displayResolution",
|
||||
"label": "displayResolution"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displayFilmGate",
|
||||
"label": "displayFilmGate"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displayFieldChart",
|
||||
"label": "displayFieldChart"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displaySafeAction",
|
||||
"label": "displaySafeAction"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displaySafeTitle",
|
||||
"label": "displaySafeTitle"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displayFilmPivot",
|
||||
"label": "displayFilmPivot"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displayFilmOrigin",
|
||||
"label": "displayFilmOrigin"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "overscan",
|
||||
"label": "overscan",
|
||||
"decimal": 1,
|
||||
"minimum": 0,
|
||||
"maximum": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -170,6 +170,10 @@
|
|||
"key": "ValidateMeshNonManifold",
|
||||
"label": "ValidateMeshNonManifold"
|
||||
},
|
||||
{
|
||||
"key": "ValidateMeshNormalsUnlocked",
|
||||
"label": "ValidateMeshNormalsUnlocked"
|
||||
},
|
||||
{
|
||||
"key": "ValidateMeshUVSetMap1",
|
||||
"label": "ValidateMeshUVSetMap1",
|
||||
|
|
@ -242,6 +246,10 @@
|
|||
"type": "label",
|
||||
"label": "Extractors"
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_maya_capture"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "load",
|
||||
"label": "Loader plugins",
|
||||
"children": [
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_loader_plugin",
|
||||
"template_data": [
|
||||
{
|
||||
"key": "LoadImage",
|
||||
"label": "Image Loader"
|
||||
},
|
||||
{
|
||||
"key": "LoadMov",
|
||||
"label": "Movie Loader"
|
||||
},
|
||||
{
|
||||
"key": "LoadSequence",
|
||||
"label": "Image Sequence Loader"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,541 @@
|
|||
[
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "ExtractPlayblast",
|
||||
"label": "Extract Playblast settings",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "capture_preset",
|
||||
"children": [
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "Codec",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "<b>Codec</b>"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "compression",
|
||||
"label": "Compression type"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "format",
|
||||
"label": "Data format"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "quality",
|
||||
"label": "Quality",
|
||||
"decimal": 0,
|
||||
"minimum": 0,
|
||||
"maximum": 100
|
||||
},
|
||||
|
||||
{
|
||||
"type": "splitter"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "Display Options",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "<b>Display Options</b>"
|
||||
},
|
||||
{
|
||||
"type": "list-strict",
|
||||
"key": "background",
|
||||
"label": "Background Color: ",
|
||||
"object_types": [
|
||||
{
|
||||
"label": "Red",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Green",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Blue",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "list-strict",
|
||||
"key": "backgroundBottom",
|
||||
"label": "Background Bottom: ",
|
||||
"object_types": [
|
||||
{
|
||||
"label": "Red",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Green",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Blue",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "list-strict",
|
||||
"key": "backgroundTop",
|
||||
"label": "Background Top: ",
|
||||
"object_types": [
|
||||
{
|
||||
"label": "Red",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Green",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
},
|
||||
{
|
||||
"label": "Blue",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"decimal": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "override_display",
|
||||
"label": "Override display options"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "Generic",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "<b>Generic</b>"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "isolate_view",
|
||||
"label": " Isolate view"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "off_screen",
|
||||
"label": " Off Screen"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "PanZoom",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "pan_zoom",
|
||||
"label": " Pan Zoom"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "Renderer",
|
||||
"children": [
|
||||
{
|
||||
"type": "label",
|
||||
"label": "<b>Renderer</b>"
|
||||
},
|
||||
{
|
||||
"type": "enum",
|
||||
"key": "rendererName",
|
||||
"label": "Renderer name",
|
||||
"enum_items": [
|
||||
{ "vp2Renderer": "Viewport 2.0" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "Resolution",
|
||||
"children": [
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"label": "<b>Resolution</b>"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "width",
|
||||
"label": " Width",
|
||||
"decimal": 0,
|
||||
"minimum": 0,
|
||||
"maximum": 99999
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "height",
|
||||
"label": "Height",
|
||||
"decimal": 0,
|
||||
"minimum": 0,
|
||||
"maximum": 99999
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "percent",
|
||||
"label": "percent",
|
||||
"decimal": 1,
|
||||
"minimum": 0,
|
||||
"maximum": 200
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"key": "mode",
|
||||
"label": "Mode"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "Viewport Options",
|
||||
"label": "Viewport Options",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "override_viewport_options",
|
||||
"label": "override_viewport_options"
|
||||
},
|
||||
{
|
||||
"type": "enum",
|
||||
"key": "displayLights",
|
||||
"label": "Display Lights",
|
||||
"enum_items": [
|
||||
{ "default": "Default Lighting"},
|
||||
{ "all": "All Lights"},
|
||||
{ "selected": "Selected Lights"},
|
||||
{ "flat": "Flat Lighting"},
|
||||
{ "nolights": "No Lights"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "textureMaxResolution",
|
||||
"label": "Texture Clamp Resolution",
|
||||
"decimal": 0
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "multiSample",
|
||||
"label": "Anti Aliasing Samples",
|
||||
"decimal": 0,
|
||||
"minimum": 0,
|
||||
"maximum": 32
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "shadows",
|
||||
"label": "Display Shadows"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "textures",
|
||||
"label": "Display Textures"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "twoSidedLighting",
|
||||
"label": "Two Sided Lighting"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "ssaoEnable",
|
||||
"label": "Screen Space Ambient Occlusion"
|
||||
},
|
||||
{
|
||||
"type": "splitter"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "cameras",
|
||||
"label": "cameras"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "clipGhosts",
|
||||
"label": "clipGhosts"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "controlVertices",
|
||||
"label": "controlVertices"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "deformers",
|
||||
"label": "deformers"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "dimensions",
|
||||
"label": "dimensions"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "dynamicConstraints",
|
||||
"label": "dynamicConstraints"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "dynamics",
|
||||
"label": "dynamics"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "fluids",
|
||||
"label": "fluids"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "follicles",
|
||||
"label": "follicles"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "gpuCacheDisplayFilter",
|
||||
"label": "gpuCacheDisplayFilter"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "greasePencils",
|
||||
"label": "greasePencils"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "grid",
|
||||
"label": "grid"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "hairSystems",
|
||||
"label": "hairSystems"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "handles",
|
||||
"label": "handles"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "hud",
|
||||
"label": "hud"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "hulls",
|
||||
"label": "hulls"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "ikHandles",
|
||||
"label": "ikHandles"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "imagePlane",
|
||||
"label": "imagePlane"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "joints",
|
||||
"label": "joints"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "lights",
|
||||
"label": "lights"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "locators",
|
||||
"label": "locators"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "manipulators",
|
||||
"label": "manipulators"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "motionTrails",
|
||||
"label": "motionTrails"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "nCloths",
|
||||
"label": "nCloths"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "nParticles",
|
||||
"label": "nParticles"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "nRigids",
|
||||
"label": "nRigids"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "nurbsCurves",
|
||||
"label": "nurbsCurves"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "nurbsSurfaces",
|
||||
"label": "nurbsSurfaces"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "particleInstancers",
|
||||
"label": "particleInstancers"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "pivots",
|
||||
"label": "pivots"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "planes",
|
||||
"label": "planes"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "pluginShapes",
|
||||
"label": "pluginShapes"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "polymeshes",
|
||||
"label": "polymeshes"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "strokes",
|
||||
"label": "strokes"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "subdivSurfaces",
|
||||
"label": "subdivSurfaces"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"key": "Camera Options",
|
||||
"label": "Camera Options",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displayGateMask",
|
||||
"label": "displayGateMask"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displayResolution",
|
||||
"label": "displayResolution"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displayFilmGate",
|
||||
"label": "displayFilmGate"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displayFieldChart",
|
||||
"label": "displayFieldChart"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displaySafeAction",
|
||||
"label": "displaySafeAction"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displaySafeTitle",
|
||||
"label": "displaySafeTitle"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displayFilmPivot",
|
||||
"label": "displayFilmPivot"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "displayFilmOrigin",
|
||||
"label": "displayFilmOrigin"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "overscan",
|
||||
"label": "overscan",
|
||||
"decimal": 1,
|
||||
"minimum": 0,
|
||||
"maximum": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
402
pype/tools/tray/pype_info_widget.py
Normal file
402
pype/tools/tray/pype_info_widget.py
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
import os
|
||||
import json
|
||||
import collections
|
||||
|
||||
from avalon import style
|
||||
from Qt import QtCore, QtGui, QtWidgets
|
||||
from pype.api import resources
|
||||
from pype.settings.lib import get_local_settings
|
||||
from pype.lib.pype_info import (
|
||||
get_all_current_info,
|
||||
get_pype_info,
|
||||
get_workstation_info,
|
||||
extract_pype_info_to_file
|
||||
)
|
||||
|
||||
IS_MAIN_ROLE = QtCore.Qt.UserRole
|
||||
|
||||
|
||||
class EnvironmentValueDelegate(QtWidgets.QStyledItemDelegate):
|
||||
def createEditor(self, parent, option, index):
|
||||
edit_widget = QtWidgets.QLineEdit(parent)
|
||||
edit_widget.setReadOnly(True)
|
||||
return edit_widget
|
||||
|
||||
|
||||
class EnvironmentsView(QtWidgets.QTreeView):
|
||||
def __init__(self, parent=None):
|
||||
super(EnvironmentsView, self).__init__(parent)
|
||||
|
||||
model = QtGui.QStandardItemModel()
|
||||
|
||||
env = os.environ.copy()
|
||||
keys = []
|
||||
values = []
|
||||
for key in sorted(env.keys()):
|
||||
key_item = QtGui.QStandardItem(key)
|
||||
key_item.setFlags(
|
||||
QtCore.Qt.ItemIsSelectable
|
||||
| QtCore.Qt.ItemIsEnabled
|
||||
)
|
||||
key_item.setData(True, IS_MAIN_ROLE)
|
||||
keys.append(key_item)
|
||||
|
||||
value = env[key]
|
||||
value_item = QtGui.QStandardItem(value)
|
||||
value_item.setData(True, IS_MAIN_ROLE)
|
||||
values.append(value_item)
|
||||
|
||||
value_parts = [
|
||||
part
|
||||
for part in value.split(os.pathsep) if part
|
||||
]
|
||||
if len(value_parts) < 2:
|
||||
continue
|
||||
|
||||
sub_parts = []
|
||||
for part_value in value_parts:
|
||||
part_item = QtGui.QStandardItem(part_value)
|
||||
part_item.setData(False, IS_MAIN_ROLE)
|
||||
sub_parts.append(part_item)
|
||||
key_item.appendRows(sub_parts)
|
||||
|
||||
model.appendColumn(keys)
|
||||
model.appendColumn(values)
|
||||
model.setHorizontalHeaderLabels(["Key", "Value"])
|
||||
|
||||
self.setModel(model)
|
||||
# self.setIndentation(0)
|
||||
delegate = EnvironmentValueDelegate(self)
|
||||
self.setItemDelegate(delegate)
|
||||
self.header().setSectionResizeMode(
|
||||
0, QtWidgets.QHeaderView.ResizeToContents
|
||||
)
|
||||
self.setSelectionMode(QtWidgets.QTreeView.ExtendedSelection)
|
||||
|
||||
def get_selection_as_dict(self):
|
||||
indexes = self.selectionModel().selectedIndexes()
|
||||
|
||||
main_mapping = collections.defaultdict(dict)
|
||||
for index in indexes:
|
||||
is_main = index.data(IS_MAIN_ROLE)
|
||||
if not is_main:
|
||||
continue
|
||||
row = index.row()
|
||||
value = index.data(QtCore.Qt.DisplayRole)
|
||||
if index.column() == 0:
|
||||
key = "key"
|
||||
else:
|
||||
key = "value"
|
||||
main_mapping[row][key] = value
|
||||
|
||||
result = {}
|
||||
for item in main_mapping.values():
|
||||
result[item["key"]] = item["value"]
|
||||
return result
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if (
|
||||
event.type() == QtGui.QKeyEvent.KeyPress
|
||||
and event.matches(QtGui.QKeySequence.Copy)
|
||||
):
|
||||
selected_data = self.get_selection_as_dict()
|
||||
selected_str = json.dumps(selected_data, indent=4)
|
||||
|
||||
mime_data = QtCore.QMimeData()
|
||||
mime_data.setText(selected_str)
|
||||
QtWidgets.QApplication.instance().clipboard().setMimeData(
|
||||
mime_data
|
||||
)
|
||||
event.accept()
|
||||
else:
|
||||
return super(EnvironmentsView, self).keyPressEvent(event)
|
||||
|
||||
|
||||
class ClickableWidget(QtWidgets.QWidget):
|
||||
clicked = QtCore.Signal()
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if event.button() == QtCore.Qt.LeftButton:
|
||||
self.clicked.emit()
|
||||
super(ClickableWidget, self).mouseReleaseEvent(event)
|
||||
|
||||
|
||||
class CollapsibleWidget(QtWidgets.QWidget):
|
||||
def __init__(self, label, parent):
|
||||
super(CollapsibleWidget, self).__init__(parent)
|
||||
|
||||
self.content_widget = None
|
||||
|
||||
top_part = ClickableWidget(parent=self)
|
||||
|
||||
button_size = QtCore.QSize(5, 5)
|
||||
button_toggle = QtWidgets.QToolButton(parent=top_part)
|
||||
button_toggle.setIconSize(button_size)
|
||||
button_toggle.setArrowType(QtCore.Qt.RightArrow)
|
||||
button_toggle.setCheckable(True)
|
||||
button_toggle.setChecked(False)
|
||||
|
||||
label_widget = QtWidgets.QLabel(label, parent=top_part)
|
||||
spacer_widget = QtWidgets.QWidget(top_part)
|
||||
|
||||
top_part_layout = QtWidgets.QHBoxLayout(top_part)
|
||||
top_part_layout.setContentsMargins(0, 0, 0, 5)
|
||||
top_part_layout.addWidget(button_toggle)
|
||||
top_part_layout.addWidget(label_widget)
|
||||
top_part_layout.addWidget(spacer_widget, 1)
|
||||
|
||||
label_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
spacer_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||
|
||||
self.button_toggle = button_toggle
|
||||
self.label_widget = label_widget
|
||||
|
||||
top_part.clicked.connect(self._top_part_clicked)
|
||||
self.button_toggle.clicked.connect(self._btn_clicked)
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(0, 0, 0, 0)
|
||||
main_layout.setSpacing(0)
|
||||
main_layout.setAlignment(QtCore.Qt.AlignTop)
|
||||
main_layout.addWidget(top_part)
|
||||
|
||||
self.main_layout = main_layout
|
||||
|
||||
def set_content_widget(self, content_widget):
|
||||
content_widget.setVisible(self.button_toggle.isChecked())
|
||||
self.main_layout.addWidget(content_widget)
|
||||
self.content_widget = content_widget
|
||||
|
||||
def _btn_clicked(self):
|
||||
self.toggle_content(self.button_toggle.isChecked())
|
||||
|
||||
def _top_part_clicked(self):
|
||||
self.toggle_content()
|
||||
|
||||
def toggle_content(self, *args):
|
||||
if len(args) > 0:
|
||||
checked = args[0]
|
||||
else:
|
||||
checked = not self.button_toggle.isChecked()
|
||||
arrow_type = QtCore.Qt.RightArrow
|
||||
if checked:
|
||||
arrow_type = QtCore.Qt.DownArrow
|
||||
self.button_toggle.setChecked(checked)
|
||||
self.button_toggle.setArrowType(arrow_type)
|
||||
if self.content_widget:
|
||||
self.content_widget.setVisible(checked)
|
||||
self.parent().updateGeometry()
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super(CollapsibleWidget, self).resizeEvent(event)
|
||||
if self.content_widget:
|
||||
self.content_widget.updateGeometry()
|
||||
|
||||
|
||||
class PypeInfoWidget(QtWidgets.QWidget):
|
||||
not_applicable = "N/A"
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super(PypeInfoWidget, self).__init__(parent)
|
||||
|
||||
self.setStyleSheet(style.load_stylesheet())
|
||||
|
||||
icon = QtGui.QIcon(resources.pype_icon_filepath())
|
||||
self.setWindowIcon(icon)
|
||||
self.setWindowTitle("Pype info")
|
||||
|
||||
main_layout = QtWidgets.QVBoxLayout(self)
|
||||
main_layout.setAlignment(QtCore.Qt.AlignTop)
|
||||
main_layout.addWidget(self._create_pype_info_widget(), 0)
|
||||
main_layout.addWidget(self._create_separator(), 0)
|
||||
main_layout.addWidget(self._create_workstation_widget(), 0)
|
||||
main_layout.addWidget(self._create_separator(), 0)
|
||||
main_layout.addWidget(self._create_local_settings_widget(), 0)
|
||||
main_layout.addWidget(self._create_separator(), 0)
|
||||
main_layout.addWidget(self._create_environ_widget(), 1)
|
||||
main_layout.addWidget(self._create_btns_section(), 0)
|
||||
|
||||
def _create_btns_section(self):
|
||||
btns_widget = QtWidgets.QWidget(self)
|
||||
btns_layout = QtWidgets.QHBoxLayout(btns_widget)
|
||||
btns_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
copy_to_clipboard_btn = QtWidgets.QPushButton(
|
||||
"Copy to clipboard", btns_widget
|
||||
)
|
||||
export_to_file_btn = QtWidgets.QPushButton(
|
||||
"Export", btns_widget
|
||||
)
|
||||
btns_layout.addWidget(QtWidgets.QWidget(btns_widget), 1)
|
||||
btns_layout.addWidget(copy_to_clipboard_btn)
|
||||
btns_layout.addWidget(export_to_file_btn)
|
||||
|
||||
copy_to_clipboard_btn.clicked.connect(self._on_copy_to_clipboard)
|
||||
export_to_file_btn.clicked.connect(self._on_export_to_file)
|
||||
|
||||
return btns_widget
|
||||
|
||||
def _on_export_to_file(self):
|
||||
dst_dir_path = QtWidgets.QFileDialog.getExistingDirectory(
|
||||
self,
|
||||
"Choose directory",
|
||||
os.path.expanduser("~"),
|
||||
QtWidgets.QFileDialog.ShowDirsOnly
|
||||
)
|
||||
if not dst_dir_path or not os.path.exists(dst_dir_path):
|
||||
return
|
||||
|
||||
filepath = extract_pype_info_to_file(dst_dir_path)
|
||||
title = "Extraction done"
|
||||
message = "Extraction is done. Destination filepath is \"{}\"".format(
|
||||
filepath.replace("\\", "/")
|
||||
)
|
||||
dialog = QtWidgets.QMessageBox(self)
|
||||
dialog.setIcon(QtWidgets.QMessageBox.NoIcon)
|
||||
dialog.setWindowTitle(title)
|
||||
dialog.setText(message)
|
||||
dialog.exec_()
|
||||
|
||||
def _on_copy_to_clipboard(self):
|
||||
all_data = get_all_current_info()
|
||||
all_data_str = json.dumps(all_data, indent=4)
|
||||
|
||||
mime_data = QtCore.QMimeData()
|
||||
mime_data.setText(all_data_str)
|
||||
QtWidgets.QApplication.instance().clipboard().setMimeData(
|
||||
mime_data
|
||||
)
|
||||
|
||||
def _create_separator(self):
|
||||
separator_widget = QtWidgets.QWidget(self)
|
||||
separator_widget.setStyleSheet("background: #222222;")
|
||||
separator_widget.setMinimumHeight(2)
|
||||
separator_widget.setMaximumHeight(2)
|
||||
return separator_widget
|
||||
|
||||
def _create_workstation_widget(self):
|
||||
key_label_mapping = {
|
||||
"system_name": "System:",
|
||||
"local_id": "Local ID:",
|
||||
"username": "Username:",
|
||||
"hostname": "Hostname:",
|
||||
"hostip": "Host IP:"
|
||||
}
|
||||
keys_order = [
|
||||
"system_name",
|
||||
"local_id",
|
||||
"username",
|
||||
"hostname",
|
||||
"hostip"
|
||||
]
|
||||
workstation_info = get_workstation_info()
|
||||
for key in workstation_info.keys():
|
||||
if key not in keys_order:
|
||||
keys_order.append(key)
|
||||
|
||||
wokstation_info_widget = CollapsibleWidget("Workstation info", self)
|
||||
|
||||
info_widget = QtWidgets.QWidget(self)
|
||||
info_layout = QtWidgets.QGridLayout(info_widget)
|
||||
# Add spacer to 3rd column
|
||||
info_layout.addWidget(QtWidgets.QWidget(info_widget), 0, 2)
|
||||
info_layout.setColumnStretch(2, 1)
|
||||
|
||||
for key in keys_order:
|
||||
if key not in workstation_info:
|
||||
continue
|
||||
|
||||
label = key_label_mapping.get(key, key)
|
||||
value = workstation_info[key]
|
||||
row = info_layout.rowCount()
|
||||
info_layout.addWidget(
|
||||
QtWidgets.QLabel(label), row, 0, 1, 1
|
||||
)
|
||||
value_label = QtWidgets.QLabel(value)
|
||||
value_label.setTextInteractionFlags(
|
||||
QtCore.Qt.TextSelectableByMouse
|
||||
)
|
||||
info_layout.addWidget(
|
||||
value_label, row, 1, 1, 1
|
||||
)
|
||||
|
||||
wokstation_info_widget.set_content_widget(info_widget)
|
||||
|
||||
return wokstation_info_widget
|
||||
|
||||
def _create_local_settings_widget(self):
|
||||
local_settings = get_local_settings()
|
||||
|
||||
local_settings_widget = CollapsibleWidget("Local settings", self)
|
||||
|
||||
settings_input = QtWidgets.QPlainTextEdit(local_settings_widget)
|
||||
settings_input.setReadOnly(True)
|
||||
settings_input.setPlainText(json.dumps(local_settings, indent=4))
|
||||
|
||||
local_settings_widget.set_content_widget(settings_input)
|
||||
|
||||
return local_settings_widget
|
||||
|
||||
def _create_environ_widget(self):
|
||||
env_widget = CollapsibleWidget("Environments", self)
|
||||
|
||||
env_view = EnvironmentsView(env_widget)
|
||||
|
||||
env_widget.set_content_widget(env_view)
|
||||
|
||||
return env_widget
|
||||
|
||||
def _create_pype_info_widget(self):
|
||||
"""Create widget with information about pype application."""
|
||||
|
||||
# Get pype info data
|
||||
pype_info = get_pype_info()
|
||||
# Modify version key/values
|
||||
version_value = "{} ({})".format(
|
||||
pype_info.pop("version", self.not_applicable),
|
||||
pype_info.pop("version_type", self.not_applicable)
|
||||
)
|
||||
pype_info["version_value"] = version_value
|
||||
# Prepare lable mapping
|
||||
key_label_mapping = {
|
||||
"version_value": "Pype version:",
|
||||
"executable": "Pype executable:",
|
||||
"pype_root": "Pype location:",
|
||||
"mongo_url": "Pype Mongo URL:"
|
||||
}
|
||||
# Prepare keys order
|
||||
keys_order = ["version_value", "executable", "pype_root", "mongo_url"]
|
||||
for key in pype_info.keys():
|
||||
if key not in keys_order:
|
||||
keys_order.append(key)
|
||||
|
||||
# Create widgets
|
||||
info_widget = QtWidgets.QWidget(self)
|
||||
info_layout = QtWidgets.QGridLayout(info_widget)
|
||||
# Add spacer to 3rd column
|
||||
info_layout.addWidget(QtWidgets.QWidget(info_widget), 0, 2)
|
||||
info_layout.setColumnStretch(2, 1)
|
||||
|
||||
title_label = QtWidgets.QLabel(info_widget)
|
||||
title_label.setText("Application information")
|
||||
title_label.setStyleSheet("font-weight: bold;")
|
||||
info_layout.addWidget(title_label, 0, 0, 1, 2)
|
||||
|
||||
for key in keys_order:
|
||||
if key not in pype_info:
|
||||
continue
|
||||
value = pype_info[key]
|
||||
label = key_label_mapping.get(key, key)
|
||||
row = info_layout.rowCount()
|
||||
info_layout.addWidget(
|
||||
QtWidgets.QLabel(label), row, 0, 1, 1
|
||||
)
|
||||
value_label = QtWidgets.QLabel(value)
|
||||
value_label.setTextInteractionFlags(
|
||||
QtCore.Qt.TextSelectableByMouse
|
||||
)
|
||||
info_layout.addWidget(
|
||||
value_label, row, 1, 1, 1
|
||||
)
|
||||
return info_widget
|
||||
|
|
@ -3,15 +3,12 @@ import sys
|
|||
|
||||
import platform
|
||||
from avalon import style
|
||||
from Qt import QtCore, QtGui, QtWidgets, QtSvg
|
||||
from Qt import QtCore, QtGui, QtWidgets
|
||||
from pype.api import Logger, resources
|
||||
from pype.modules import TrayModulesManager, ITrayService
|
||||
from pype.settings.lib import get_system_settings
|
||||
import pype.version
|
||||
try:
|
||||
import configparser
|
||||
except Exception:
|
||||
import ConfigParser as configparser
|
||||
from .pype_info_widget import PypeInfoWidget
|
||||
|
||||
|
||||
class TrayManager:
|
||||
|
|
@ -19,13 +16,14 @@ class TrayManager:
|
|||
|
||||
Load submenus, actions, separators and modules into tray's context.
|
||||
"""
|
||||
available_sourcetypes = ["python", "file"]
|
||||
|
||||
def __init__(self, tray_widget, main_window):
|
||||
self.tray_widget = tray_widget
|
||||
self.main_window = main_window
|
||||
|
||||
self.log = Logger().get_logger(self.__class__.__name__)
|
||||
self.pype_info_widget = None
|
||||
|
||||
self.log = Logger.get_logger(self.__class__.__name__)
|
||||
|
||||
self.module_settings = get_system_settings()["modules"]
|
||||
|
||||
|
|
@ -36,7 +34,7 @@ class TrayManager:
|
|||
def initialize_modules(self):
|
||||
"""Add modules to tray."""
|
||||
|
||||
self.modules_manager.initialize(self.tray_widget.menu)
|
||||
self.modules_manager.initialize(self, self.tray_widget.menu)
|
||||
|
||||
# Add services if they are
|
||||
services_submenu = ITrayService.services_submenu(self.tray_widget.menu)
|
||||
|
|
@ -58,6 +56,26 @@ class TrayManager:
|
|||
# Print time report
|
||||
self.modules_manager.print_report()
|
||||
|
||||
def show_tray_message(self, title, message, icon=None, msecs=None):
|
||||
"""Show tray message.
|
||||
|
||||
Args:
|
||||
title (str): Title of message.
|
||||
message (str): Content of message.
|
||||
icon (QSystemTrayIcon.MessageIcon): Message's icon. Default is
|
||||
Information icon, may differ by Qt version.
|
||||
msecs (int): Duration of message visibility in miliseconds.
|
||||
Default is 10000 msecs, may differ by Qt version.
|
||||
"""
|
||||
args = [title, message]
|
||||
kwargs = {}
|
||||
if icon:
|
||||
kwargs["icon"] = icon
|
||||
if msecs:
|
||||
kwargs["msecs"] = msecs
|
||||
|
||||
self.tray_widget.showMessage(*args, **kwargs)
|
||||
|
||||
def _add_version_item(self):
|
||||
subversion = os.environ.get("PYPE_SUBVERSION")
|
||||
client_name = os.environ.get("PYPE_CLIENT")
|
||||
|
|
@ -70,12 +88,21 @@ class TrayManager:
|
|||
version_string += ", {}".format(client_name)
|
||||
|
||||
version_action = QtWidgets.QAction(version_string, self.tray_widget)
|
||||
version_action.triggered.connect(self._on_version_action)
|
||||
self.tray_widget.menu.addAction(version_action)
|
||||
self.tray_widget.menu.addSeparator()
|
||||
|
||||
def on_exit(self):
|
||||
self.modules_manager.on_exit()
|
||||
|
||||
def _on_version_action(self):
|
||||
if self.pype_info_widget is None:
|
||||
self.pype_info_widget = PypeInfoWidget()
|
||||
|
||||
self.pype_info_widget.show()
|
||||
self.pype_info_widget.raise_()
|
||||
self.pype_info_widget.activateWindow()
|
||||
|
||||
|
||||
class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
||||
"""Tray widget.
|
||||
|
|
@ -85,9 +112,9 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
|||
"""
|
||||
|
||||
def __init__(self, parent):
|
||||
self.icon = QtGui.QIcon(resources.pype_icon_filepath())
|
||||
icon = QtGui.QIcon(resources.pype_icon_filepath())
|
||||
|
||||
QtWidgets.QSystemTrayIcon.__init__(self, self.icon, parent)
|
||||
super(SystemTrayIcon, self).__init__(icon, parent)
|
||||
|
||||
# Store parent - QtWidgets.QMainWindow()
|
||||
self.parent = parent
|
||||
|
|
@ -100,15 +127,15 @@ class SystemTrayIcon(QtWidgets.QSystemTrayIcon):
|
|||
self.tray_man = TrayManager(self, self.parent)
|
||||
self.tray_man.initialize_modules()
|
||||
|
||||
# Catch activate event
|
||||
self.activated.connect(self.on_systray_activated)
|
||||
# Catch activate event for left click if not on MacOS
|
||||
# - MacOS has this ability by design so menu would be doubled
|
||||
if platform.system().lower() != "darwin":
|
||||
self.activated.connect(self.on_systray_activated)
|
||||
# Add menu to Context of SystemTrayIcon
|
||||
self.setContextMenu(self.menu)
|
||||
|
||||
def on_systray_activated(self, reason):
|
||||
# show contextMenu if left click
|
||||
if platform.system().lower() == "darwin":
|
||||
return
|
||||
if reason == QtWidgets.QSystemTrayIcon.Trigger:
|
||||
position = QtGui.QCursor().pos()
|
||||
self.contextMenu().popup(position)
|
||||
|
|
@ -128,119 +155,24 @@ class TrayMainWindow(QtWidgets.QMainWindow):
|
|||
|
||||
Every widget should have set this window as parent because
|
||||
QSystemTrayIcon widget is not allowed to be a parent of any widget.
|
||||
|
||||
:param app: Qt application manages application's control flow
|
||||
:type app: QtWidgets.QApplication
|
||||
|
||||
.. note::
|
||||
*TrayMainWindow* has ability to show **working** widget.
|
||||
Calling methods:
|
||||
- ``show_working()``
|
||||
- ``hide_working()``
|
||||
.. todo:: Hide working widget if idle is too long
|
||||
"""
|
||||
|
||||
def __init__(self, app):
|
||||
super().__init__()
|
||||
super(TrayMainWindow, self).__init__()
|
||||
self.app = app
|
||||
|
||||
self.set_working_widget()
|
||||
|
||||
self.trayIcon = SystemTrayIcon(self)
|
||||
self.trayIcon.show()
|
||||
|
||||
def set_working_widget(self):
|
||||
image_file = resources.get_resource("icons", "working.svg")
|
||||
img_pix = QtGui.QPixmap(image_file)
|
||||
if image_file.endswith('.svg'):
|
||||
widget = QtSvg.QSvgWidget(image_file)
|
||||
else:
|
||||
widget = QtWidgets.QLabel()
|
||||
widget.setPixmap(img_pix)
|
||||
|
||||
# Set widget properties
|
||||
widget.setGeometry(img_pix.rect())
|
||||
widget.setMask(img_pix.mask())
|
||||
widget.setWindowFlags(
|
||||
QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.FramelessWindowHint
|
||||
)
|
||||
widget.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
|
||||
|
||||
self.center_widget(widget)
|
||||
self._working_widget = widget
|
||||
self.helper = DragAndDropHelper(self._working_widget)
|
||||
|
||||
def center_widget(self, widget):
|
||||
frame_geo = widget.frameGeometry()
|
||||
screen = self.app.desktop().cursor().pos()
|
||||
center_point = self.app.desktop().screenGeometry(
|
||||
self.app.desktop().screenNumber(screen)
|
||||
).center()
|
||||
frame_geo.moveCenter(center_point)
|
||||
widget.move(frame_geo.topLeft())
|
||||
|
||||
def show_working(self):
|
||||
self._working_widget.show()
|
||||
|
||||
def hide_working(self):
|
||||
self.center_widget(self._working_widget)
|
||||
self._working_widget.hide()
|
||||
|
||||
|
||||
class DragAndDropHelper:
|
||||
""" Helper adds to widget drag and drop ability
|
||||
|
||||
:param widget: Qt Widget where drag and drop ability will be added
|
||||
"""
|
||||
|
||||
def __init__(self, widget):
|
||||
self.widget = widget
|
||||
self.widget.mousePressEvent = self.mousePressEvent
|
||||
self.widget.mouseMoveEvent = self.mouseMoveEvent
|
||||
self.widget.mouseReleaseEvent = self.mouseReleaseEvent
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
self.__mousePressPos = None
|
||||
self.__mouseMovePos = None
|
||||
if event.button() == QtCore.Qt.LeftButton:
|
||||
self.__mousePressPos = event.globalPos()
|
||||
self.__mouseMovePos = event.globalPos()
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
if event.buttons() == QtCore.Qt.LeftButton:
|
||||
# adjust offset from clicked point to origin of widget
|
||||
currPos = self.widget.mapToGlobal(
|
||||
self.widget.pos()
|
||||
)
|
||||
globalPos = event.globalPos()
|
||||
diff = globalPos - self.__mouseMovePos
|
||||
newPos = self.widget.mapFromGlobal(currPos + diff)
|
||||
self.widget.move(newPos)
|
||||
self.__mouseMovePos = globalPos
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if self.__mousePressPos is not None:
|
||||
moved = event.globalPos() - self.__mousePressPos
|
||||
if moved.manhattanLength() > 3:
|
||||
event.ignore()
|
||||
return
|
||||
self.tray_widget = SystemTrayIcon(self)
|
||||
self.tray_widget.show()
|
||||
|
||||
|
||||
class PypeTrayApplication(QtWidgets.QApplication):
|
||||
"""Qt application manages application's control flow."""
|
||||
|
||||
def __init__(self):
|
||||
super(self.__class__, self).__init__(sys.argv)
|
||||
super(PypeTrayApplication, self).__init__(sys.argv)
|
||||
# Allows to close widgets without exiting app
|
||||
self.setQuitOnLastWindowClosed(False)
|
||||
|
||||
# Allow show icon istead of python icon in task bar (Windows)
|
||||
if os.name == "nt":
|
||||
import ctypes
|
||||
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
|
||||
u"pype_tray"
|
||||
)
|
||||
|
||||
# Sets up splash
|
||||
splash_widget = self.set_splash()
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ appdirs = "^1.4.3"
|
|||
blessed = "^1.17" # pype terminal formatting
|
||||
clique = "1.5.*"
|
||||
Click = "^7"
|
||||
dnspython = "^2.1.0"
|
||||
ftrack-python-api = "2.0.*"
|
||||
google-api-python-client = "^1.12.8" # sync server google support (should be separate?)
|
||||
jsonschema = "^3.2.0"
|
||||
|
|
@ -52,7 +53,7 @@ Jinja2 = "^2.11"
|
|||
pycodestyle = "^2.5.0"
|
||||
pydocstyle = "^3.0.0"
|
||||
pylint = "^2.4.4"
|
||||
pytest = "^5.3.2"
|
||||
pytest = "^6.1"
|
||||
pytest-cov = "*"
|
||||
pytest-print = "*"
|
||||
Sphinx = "*"
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit eae14f2960c4ccf2f0211e0726e88563129c0296
|
||||
Subproject commit 9e6b0d02e5a147cbafdcaeee7d786d4767e14c94
|
||||
125
start.py
125
start.py
|
|
@ -112,7 +112,7 @@ if getattr(sys, 'frozen', False):
|
|||
os.environ["PYTHONPATH"] = os.pathsep.join(paths)
|
||||
|
||||
from igniter import BootstrapRepos # noqa: E402
|
||||
from igniter.tools import load_environments # noqa: E402
|
||||
from igniter.tools import get_pype_path_from_db # noqa
|
||||
from igniter.bootstrap_repos import PypeVersion # noqa: E402
|
||||
|
||||
bootstrap = BootstrapRepos()
|
||||
|
|
@ -120,32 +120,26 @@ silent_commands = ["run", "igniter", "standalonepublisher",
|
|||
"extractenvironments"]
|
||||
|
||||
|
||||
def set_environments() -> None:
|
||||
"""Set loaded environments.
|
||||
def set_pype_global_environments() -> None:
|
||||
"""Set global pype's environments."""
|
||||
import acre
|
||||
|
||||
.. todo:
|
||||
better handling of environments
|
||||
from pype.settings import get_environments
|
||||
|
||||
"""
|
||||
try:
|
||||
import acre
|
||||
except ImportError:
|
||||
if getattr(sys, 'frozen', False):
|
||||
sys.path.append(os.path.join(
|
||||
os.path.dirname(sys.executable),
|
||||
"dependencies"
|
||||
))
|
||||
import acre
|
||||
try:
|
||||
env = load_environments(["global"])
|
||||
except OSError as e:
|
||||
print(f"!!! {e}")
|
||||
sys.exit(1)
|
||||
all_env = get_environments()
|
||||
|
||||
env = acre.merge(env, dict(os.environ))
|
||||
# TODO Global environments will be stored in "general" settings so loading
|
||||
# will be modified and can be done in igniter.
|
||||
env = acre.merge(all_env["global"], dict(os.environ))
|
||||
os.environ.clear()
|
||||
os.environ.update(env)
|
||||
|
||||
# Hardcoded default values
|
||||
os.environ["PYBLISH_GUI"] = "pyblish_pype"
|
||||
# Change scale factor only if is not set
|
||||
if "QT_AUTO_SCREEN_SCALE_FACTOR" not in os.environ:
|
||||
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
|
||||
|
||||
|
||||
def run(arguments: list, env: dict = None) -> int:
|
||||
"""Use correct executable to run stuff.
|
||||
|
|
@ -269,9 +263,13 @@ def _process_arguments() -> tuple:
|
|||
# handle igniter
|
||||
# this is helper to run igniter before anything else
|
||||
if "igniter" in sys.argv:
|
||||
pass
|
||||
# import igniter
|
||||
# igniter.run()
|
||||
import igniter
|
||||
return_code = igniter.open_dialog()
|
||||
|
||||
# this is when we want to run Pype without installing anything.
|
||||
# or we are ready to run.
|
||||
if return_code not in [2, 3]:
|
||||
sys.exit(return_code)
|
||||
|
||||
return use_version, use_staging
|
||||
|
||||
|
|
@ -298,7 +296,9 @@ def _determine_mongodb() -> str:
|
|||
except ValueError:
|
||||
print("*** No DB connection string specified.")
|
||||
print("--- launching setup UI ...")
|
||||
run(["igniter"])
|
||||
import igniter
|
||||
igniter.open_dialog()
|
||||
|
||||
try:
|
||||
pype_mongo = bootstrap.registry.get_secure_item("pypeMongo")
|
||||
except ValueError:
|
||||
|
|
@ -364,19 +364,41 @@ def _find_frozen_pype(use_version: str = None,
|
|||
pype_version = None
|
||||
pype_versions = bootstrap.find_pype(include_zips=True,
|
||||
staging=use_staging)
|
||||
try:
|
||||
# use latest one found (last in the list is latest)
|
||||
pype_version = pype_versions[-1]
|
||||
except IndexError:
|
||||
# no pype version found, run Igniter and ask for them.
|
||||
print('*** No Pype versions found.')
|
||||
print("--- launching setup UI ...")
|
||||
run(["igniter"])
|
||||
pype_versions = bootstrap.find_pype()
|
||||
if not os.getenv("PYPE_TRYOUT"):
|
||||
try:
|
||||
# use latest one found (last in the list is latest)
|
||||
pype_version = pype_versions[-1]
|
||||
except IndexError:
|
||||
# no pype version found, run Igniter and ask for them.
|
||||
print('*** No Pype versions found.')
|
||||
print("--- launching setup UI ...")
|
||||
import igniter
|
||||
return_code = igniter.open_dialog()
|
||||
if return_code == 2:
|
||||
os.environ["PYPE_TRYOUT"] = "1"
|
||||
if return_code == 3:
|
||||
# run Pype after installation
|
||||
|
||||
print('>>> Finding Pype again ...')
|
||||
pype_versions = bootstrap.find_pype(staging=use_staging)
|
||||
try:
|
||||
pype_version = pype_versions[-1]
|
||||
except IndexError:
|
||||
print(("!!! Something is wrong and we didn't "
|
||||
"found it again."))
|
||||
pype_versions = None
|
||||
sys.exit(1)
|
||||
elif return_code != 2:
|
||||
print(f" . finished ({return_code})")
|
||||
sys.exit(return_code)
|
||||
|
||||
if not pype_versions:
|
||||
# no Pype versions found anyway, lets use then the one
|
||||
# shipped with frozen Pype
|
||||
if not os.getenv("PYPE_TRYOUT"):
|
||||
print("*** Still no luck finding Pype.")
|
||||
print(("*** We'll try to use the one coming "
|
||||
"with Pype installation."))
|
||||
version_path = _bootstrap_from_code(use_version)
|
||||
pype_version = PypeVersion(
|
||||
version=BootstrapRepos.get_version(version_path),
|
||||
|
|
@ -440,6 +462,8 @@ def _bootstrap_from_code(use_version):
|
|||
pype_root = os.path.normpath(
|
||||
os.path.dirname(sys.executable))
|
||||
local_version = bootstrap.get_version(Path(pype_root))
|
||||
print(f" - running version: {local_version}")
|
||||
assert local_version
|
||||
else:
|
||||
pype_root = os.path.normpath(
|
||||
os.path.dirname(os.path.realpath(__file__)))
|
||||
|
|
@ -497,7 +521,6 @@ def _bootstrap_from_code(use_version):
|
|||
|
||||
def boot():
|
||||
"""Bootstrap Pype."""
|
||||
version_path = None
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Play animation
|
||||
|
|
@ -529,7 +552,7 @@ def boot():
|
|||
os.environ["PYPE_MONGO"] = pype_mongo
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Load environments from database
|
||||
# Set environments - load Pype path from database (if set)
|
||||
# ------------------------------------------------------------------------
|
||||
# set PYPE_ROOT to running location until proper version can be
|
||||
# determined.
|
||||
|
|
@ -537,7 +560,12 @@ def boot():
|
|||
os.environ["PYPE_ROOT"] = os.path.dirname(sys.executable)
|
||||
else:
|
||||
os.environ["PYPE_ROOT"] = os.path.dirname(__file__)
|
||||
set_environments()
|
||||
|
||||
# Get Pype path from database and set it to environment so Pype can
|
||||
# find its versions there and bootstrap them.
|
||||
pype_path = get_pype_path_from_db(pype_mongo)
|
||||
if not os.getenv("PYPE_PATH") and pype_path:
|
||||
os.environ["PYPE_PATH"] = pype_path
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Find Pype versions
|
||||
|
|
@ -566,10 +594,12 @@ def boot():
|
|||
|
||||
# delete Pype module and it's submodules from cache so it is used from
|
||||
# specific version
|
||||
modules_to_del = []
|
||||
for module_name in tuple(sys.modules):
|
||||
if module_name == "pype" or module_name.startswith("pype."):
|
||||
modules_to_del.append(sys.modules.pop(module_name))
|
||||
modules_to_del = [
|
||||
sys.modules.pop(module_name)
|
||||
for module_name in tuple(sys.modules)
|
||||
if module_name == "pype" or module_name.startswith("pype.")
|
||||
]
|
||||
|
||||
try:
|
||||
for module_name in modules_to_del:
|
||||
del sys.modules[module_name]
|
||||
|
|
@ -582,8 +612,12 @@ def boot():
|
|||
from pype.lib import terminal as t
|
||||
from pype.version import __version__
|
||||
print(">>> loading environments ...")
|
||||
# Must happen before `set_modules_environments`
|
||||
# Avalon environments must be set before avalon module is imported
|
||||
print(" - for Avalon ...")
|
||||
set_avalon_environments()
|
||||
print(" - global Pype ...")
|
||||
set_pype_global_environments()
|
||||
print(" - for modules ...")
|
||||
set_modules_environments()
|
||||
|
||||
assert version_path, "Version path not defined."
|
||||
|
|
@ -593,10 +627,7 @@ def boot():
|
|||
t_width = 20
|
||||
try:
|
||||
t_width = os.get_terminal_size().columns - 2
|
||||
except ValueError:
|
||||
# running without terminal
|
||||
pass
|
||||
except OSError:
|
||||
except (ValueError, OSError):
|
||||
# running without terminal
|
||||
pass
|
||||
|
||||
|
|
@ -610,7 +641,7 @@ def boot():
|
|||
|
||||
try:
|
||||
cli.main(obj={}, prog_name="pype")
|
||||
except Exception:
|
||||
except Exception: # noqa
|
||||
exc_info = sys.exc_info()
|
||||
print("!!! Pype crashed:")
|
||||
traceback.print_exception(*exc_info)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ def test_pype_version():
|
|||
|
||||
v2 = PypeVersion(1, 2, 3, client="x")
|
||||
assert str(v2) == "1.2.3-x"
|
||||
assert v1 < v2
|
||||
|
||||
v3 = PypeVersion(1, 2, 3, variant="staging")
|
||||
assert str(v3) == "1.2.3-staging"
|
||||
|
|
@ -35,6 +36,7 @@ def test_pype_version():
|
|||
v4 = PypeVersion(1, 2, 3, variant="staging", client="client")
|
||||
assert str(v4) == "1.2.3-client-staging"
|
||||
assert v3 < v4
|
||||
assert v1 < v4
|
||||
|
||||
v5 = PypeVersion(1, 2, 3, variant="foo", client="x")
|
||||
assert str(v5) == "1.2.3-x"
|
||||
|
|
@ -55,6 +57,9 @@ def test_pype_version():
|
|||
v10 = PypeVersion(1, 2, 2)
|
||||
assert v10 < v1
|
||||
|
||||
v11 = PypeVersion(1, 2, 3, path=Path("/foo/bar"))
|
||||
assert v10 < v11
|
||||
|
||||
assert v5 == v2
|
||||
|
||||
sort_versions = [
|
||||
|
|
@ -141,7 +146,7 @@ def test_search_string_for_pype_version(printer):
|
|||
|
||||
|
||||
def test_install_live_repos(fix_bootstrap, printer):
|
||||
rf = fix_bootstrap.install_live_repos()
|
||||
rf = fix_bootstrap.create_version_from_live_code()
|
||||
sep = os.path.sep
|
||||
expected_paths = [
|
||||
f"{rf}{sep}avalon-core",
|
||||
|
|
|
|||
|
|
@ -95,11 +95,16 @@ libs_dir = build_dir / "lib"
|
|||
|
||||
to_delete = []
|
||||
_print("Finding duplicates ...")
|
||||
deps_items = list(deps_dir.iterdir())
|
||||
for d in libs_dir.iterdir():
|
||||
if (deps_dir / d.name) in deps_dir.iterdir():
|
||||
if (deps_dir / d.name) in deps_items:
|
||||
to_delete.append(d)
|
||||
_print(f"found {d}", 3)
|
||||
|
||||
# add pype and igniter in libs too
|
||||
to_delete.append(libs_dir / "pype")
|
||||
to_delete.append(libs_dir / "igniter")
|
||||
|
||||
# delete duplicates
|
||||
_print(f"Deleting {len(to_delete)} duplicates ...")
|
||||
for d in to_delete:
|
||||
|
|
|
|||
41
vendor/deadline/custom/plugins/Pype/Pype.options
vendored
Normal file
41
vendor/deadline/custom/plugins/Pype/Pype.options
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
[ScriptFile]
|
||||
Type=filename
|
||||
Label=Script File
|
||||
Category=Python Options
|
||||
CategoryOrder=0
|
||||
Index=0
|
||||
Description=The script file to be executed.
|
||||
Required=false
|
||||
DisableIfBlank=true
|
||||
|
||||
[Arguments]
|
||||
Type=string
|
||||
Label=Arguments
|
||||
Category=Python Options
|
||||
CategoryOrder=0
|
||||
Index=1
|
||||
Description=The arguments to pass to the script. If no arguments are required, leave this blank.
|
||||
Required=false
|
||||
DisableIfBlank=true
|
||||
|
||||
[Version]
|
||||
Type=enum
|
||||
Values=3.0
|
||||
Label=Version
|
||||
Category=Python Options
|
||||
CategoryOrder=0
|
||||
Index=2
|
||||
Description=The version of Python to use.
|
||||
Required=false
|
||||
DisableIfBlank=true
|
||||
|
||||
[SingleFramesOnly]
|
||||
Type=boolean
|
||||
Label=Single Frames Only
|
||||
Category=Job Options
|
||||
CategoryOrder=1
|
||||
Index=0
|
||||
Description=If enabled, the plugin will only render one frame at a time even if a single task contains a chunk of frames.
|
||||
Required=true
|
||||
DisableIfBlank=true
|
||||
Default=false
|
||||
Loading…
Add table
Add a link
Reference in a new issue