mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-02 17:04:54 +01:00
Merge branch 'develop' into feature/settings_entities
This commit is contained in:
commit
d04007f962
321 changed files with 8019 additions and 1634 deletions
9
.gitmodules
vendored
9
.gitmodules
vendored
|
|
@ -2,22 +2,15 @@
|
|||
path = repos/avalon-core
|
||||
url = git@github.com:pypeclub/avalon-core.git
|
||||
branch = develop
|
||||
[submodule "repos/pyblish-base"]
|
||||
path = repos/pyblish-base
|
||||
url = git@github.com:pyblish/pyblish-base.git
|
||||
[submodule "repos/avalon-unreal-integration"]
|
||||
path = repos/avalon-unreal-integration
|
||||
url = git@github.com:pypeclub/avalon-unreal-integration.git
|
||||
[submodule "repos/maya-look-assigner"]
|
||||
path = repos/maya-look-assigner
|
||||
url = git@github.com:pypeclub/maya-look-assigner.git
|
||||
[submodule "repos/acre"]
|
||||
path = repos/acre
|
||||
url = git@github.com:antirotor/acre.git
|
||||
branch = fix/unformatted-tokens
|
||||
[submodule "pype/modules/ftrack/python2_vendor/ftrack-python-api"]
|
||||
path = pype/modules/ftrack/python2_vendor/ftrack-python-api
|
||||
url = https://bitbucket.org/ftrack/ftrack-python-api.git
|
||||
[submodule "pype/modules/ftrack/python2_vendor/arrow"]
|
||||
path = pype/modules/ftrack/python2_vendor/arrow
|
||||
url = git@github.com:arrow-py/arrow.git
|
||||
url = git@github.com:arrow-py/arrow.git
|
||||
97
README.md
97
README.md
|
|
@ -1,33 +1,106 @@
|
|||
# Pype
|
||||
|
||||
## Introduction
|
||||
Pype
|
||||
====
|
||||
|
||||
Multi-platform open-source pipeline built around the [Avalon](https://getavalon.github.io/) platform, expanding it with extra features and integrations. Pype connects asset database, project management and time tracking into a single modular system. It has tight integration with [ftrack](https://www.ftrack.com/en/), but it can also run independently.
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Multi-platform open-source pipeline built around the [Avalon](https://getavalon.github.io/) platform,
|
||||
expanding it with extra features and integrations. Pype connects asset database, project management
|
||||
and time tracking into a single modular system. It has tight integration
|
||||
with [ftrack](https://www.ftrack.com/en/), but it can also run independently.
|
||||
|
||||
To get all the key information about the project, go to [PYPE.club](http://pype.club)
|
||||
|
||||
## Hardware requirements
|
||||
Requirements
|
||||
------------
|
||||
Pype will run on most typical hardware configurations commonly found in studios around the world.
|
||||
It is installed on artist computer and can take up 3Gb of space depending on number of versions
|
||||
and other dependencies.
|
||||
|
||||
Pype should be installed centrally on a fast network storage with at least read access right for all workstations and users in the Studio. Full Deplyoyment with all dependencies and both Development and Production branches installed takes about 1GB of data, however to ensure smooth updates and general working comfort, we recommend allocating at least at least 4GB of storage dedicated to PYPE deployment.
|
||||
For well functioning [ftrack](https://www.ftrack.com/en/) event server, we recommend a
|
||||
linux virtual server with [Ubuntu](https://ubuntu.com/) or [CentosOS](https://www.centos.org/).
|
||||
CPU and RAM allocation need differ based on the studio size, but a 2GB of RAM, with a
|
||||
dual core CPU and around 4GB of storage should suffice.
|
||||
|
||||
For well functioning [ftrack](https://www.ftrack.com/en/) event server, we recommend a linux virtual server with [Ubuntu](https://ubuntu.com/) or [CentosOS](https://www.centos.org/). CPU and RAM allocation need differ based on the studio size, but a 2GB of RAM, with a dual core CPU and around 4GB of storage should suffice.
|
||||
Pype needs running [mongodb](https://www.mongodb.com/) server with good connectivity as it is
|
||||
heavily used by Pype. Depending on project size and number of artists working connection speed and
|
||||
latency influence performance experienced by artists. If remote working is required, this mongodb
|
||||
server must be accessible from Internet or cloud solution can be used. Reasonable backup plan
|
||||
or high availability options are recommended.
|
||||
|
||||
## Building Pype
|
||||
Building Pype
|
||||
-------------
|
||||
|
||||
### Windows
|
||||
|
||||
You will need [Python 3.7 and newer](https://www.python.org/downloads/) and [git](https://git-scm.com/downloads).
|
||||
More tools might be needed for installing dependencies (for example for **OpenTimelineIO**) - mostly
|
||||
development tools like [CMake](https://cmake.org/) and [Visual Studio](https://visualstudio.microsoft.com/cs/downloads/)
|
||||
|
||||
Clone repository:
|
||||
```sh
|
||||
git clone --recurse-submodules git@github.com:pypeclub/pype.git
|
||||
```
|
||||
|
||||
Run PowerShell script `build.ps1`. It will create *venv*, install all
|
||||
required dependencies and build Pype. After it is finished, you will find
|
||||
Pype in `build` folder.
|
||||
#### To build Pype:
|
||||
|
||||
You might need more tools for installing dependencies (for example for **OpenTimelineIO**) - mostly
|
||||
development tools like [CMake](https://cmake.org/) and [Visual Studio](https://visualstudio.microsoft.com/cs/downloads/)
|
||||
1) Run `.\tools\create_env.ps1` to create virtual environment in `.\venv`
|
||||
2) Run `.\tools\build.ps1` to build pype executables in `.\build\`
|
||||
|
||||
To create distributable Pype versions, run `./tools/create_zip.ps1` - that will
|
||||
create zip file with name `pype-vx.x.x.zip` parsed from current pype repository and
|
||||
copy it to user data dir, or you can specify `--path /path/to/zip` to force it there.
|
||||
|
||||
You can then point **Igniter** - Pype setup tool - to directory containing this zip and
|
||||
it will install it on current computer.
|
||||
|
||||
Pype is build using [CX_Freeze](https://cx-freeze.readthedocs.io/en/latest) to freeze itself and all dependencies.
|
||||
|
||||
Running Pype
|
||||
------------
|
||||
|
||||
Pype can by executed either from live sources (this repository) or from
|
||||
*"frozen code"* - executables that can be build using steps described above.
|
||||
|
||||
If Pype is executed from live sources, it will use Pype version included in them. If
|
||||
it is executed from frozen code it will try to find latest Pype version installed locally
|
||||
on current computer and if it is not found, it will ask for its location. On that location
|
||||
pype can be either in directories or zip files. Pype will try to find latest version and
|
||||
install it to user data directory (on Windows to `%LOCALAPPDATA%\pypeclub\pype`).
|
||||
|
||||
### From sources
|
||||
Pype can be run directly from sources by activating virtual environment:
|
||||
```powershell
|
||||
.\venv\Scripts\Activate.ps1
|
||||
```
|
||||
and running:
|
||||
```powershell
|
||||
python start.py tray
|
||||
```
|
||||
This will use current Pype version with sources. You can override this with `--use-version=x.x.x` and
|
||||
then Pype will try to find locally installed specified version (present in user data directory).
|
||||
|
||||
### From frozen code
|
||||
|
||||
You need to build Pype first. This will produce two executables - `pype.exe` and `pype_console.exe`.
|
||||
First one will act as GUI application and will not create console (useful in production environments).
|
||||
The second one will create console and will write output there - useful for headless application and
|
||||
debugging purposes. If you need pype version installed, just run `./tools/create_zip.ps1` without
|
||||
arguments and it will create zip file that pype can use.
|
||||
|
||||
|
||||
Building documentation
|
||||
----------------------
|
||||
|
||||
Top build API documentation, run `.\tools\make_docs.ps1`. It will create html documentation
|
||||
from current sources in `.\docs\build`.
|
||||
|
||||
**Note that it needs existing virtual environment.**
|
||||
|
||||
Running tests
|
||||
-------------
|
||||
|
||||
To run tests, execute `.\tools\run_tests.ps1`.
|
||||
|
||||
**Note that it needs existing virtual environment.**
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
.\venv\Scripts\Activate.ps1
|
||||
python pype.py mongodb
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
.\venv\Scripts\Activate.ps1
|
||||
python pype.py settings --dev
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
.\venv\Scripts\Activate.ps1
|
||||
python pype.py tray --debug
|
||||
7
docs/source/pype.hosts.aftereffects.rst
Normal file
7
docs/source/pype.hosts.aftereffects.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.hosts.aftereffects package
|
||||
===============================
|
||||
|
||||
.. automodule:: pype.hosts.aftereffects
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.hosts.resolve.otio.davinci_export.rst
Normal file
7
docs/source/pype.hosts.resolve.otio.davinci_export.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.hosts.resolve.otio.davinci\_export module
|
||||
==============================================
|
||||
|
||||
.. automodule:: pype.hosts.resolve.otio.davinci_export
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.hosts.resolve.otio.davinci_import.rst
Normal file
7
docs/source/pype.hosts.resolve.otio.davinci_import.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.hosts.resolve.otio.davinci\_import module
|
||||
==============================================
|
||||
|
||||
.. automodule:: pype.hosts.resolve.otio.davinci_import
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
17
docs/source/pype.hosts.resolve.otio.rst
Normal file
17
docs/source/pype.hosts.resolve.otio.rst
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
pype.hosts.resolve.otio package
|
||||
===============================
|
||||
|
||||
.. automodule:: pype.hosts.resolve.otio
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 10
|
||||
|
||||
pype.hosts.resolve.otio.davinci_export
|
||||
pype.hosts.resolve.otio.davinci_import
|
||||
pype.hosts.resolve.otio.utils
|
||||
7
docs/source/pype.hosts.resolve.otio.utils.rst
Normal file
7
docs/source/pype.hosts.resolve.otio.utils.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.hosts.resolve.otio.utils module
|
||||
====================================
|
||||
|
||||
.. automodule:: pype.hosts.resolve.otio.utils
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.hosts.resolve.todo-rendering.rst
Normal file
7
docs/source/pype.hosts.resolve.todo-rendering.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.hosts.resolve.todo\-rendering module
|
||||
=========================================
|
||||
|
||||
.. automodule:: pype.hosts.resolve.todo-rendering
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.hosts.tvpaint.api.rst
Normal file
7
docs/source/pype.hosts.tvpaint.api.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.hosts.tvpaint.api package
|
||||
==============================
|
||||
|
||||
.. automodule:: pype.hosts.tvpaint.api
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
15
docs/source/pype.hosts.tvpaint.rst
Normal file
15
docs/source/pype.hosts.tvpaint.rst
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
pype.hosts.tvpaint package
|
||||
==========================
|
||||
|
||||
.. automodule:: pype.hosts.tvpaint
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Subpackages
|
||||
-----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 10
|
||||
|
||||
pype.hosts.tvpaint.api
|
||||
7
docs/source/pype.lib.abstract_collect_render.rst
Normal file
7
docs/source/pype.lib.abstract_collect_render.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.abstract\_collect\_render module
|
||||
=========================================
|
||||
|
||||
.. automodule:: pype.lib.abstract_collect_render
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.lib.abstract_expected_files.rst
Normal file
7
docs/source/pype.lib.abstract_expected_files.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.abstract\_expected\_files module
|
||||
=========================================
|
||||
|
||||
.. automodule:: pype.lib.abstract_expected_files
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.lib.abstract_metaplugins.rst
Normal file
7
docs/source/pype.lib.abstract_metaplugins.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.abstract\_metaplugins module
|
||||
=====================================
|
||||
|
||||
.. automodule:: pype.lib.abstract_metaplugins
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.lib.abstract_submit_deadline.rst
Normal file
7
docs/source/pype.lib.abstract_submit_deadline.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.abstract\_submit\_deadline module
|
||||
==========================================
|
||||
|
||||
.. automodule:: pype.lib.abstract_submit_deadline
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.lib.applications.rst
Normal file
7
docs/source/pype.lib.applications.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.applications module
|
||||
============================
|
||||
|
||||
.. automodule:: pype.lib.applications
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.lib.avalon_context.rst
Normal file
7
docs/source/pype.lib.avalon_context.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.avalon\_context module
|
||||
===============================
|
||||
|
||||
.. automodule:: pype.lib.avalon_context
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.lib.deprecated.rst
Normal file
7
docs/source/pype.lib.deprecated.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.deprecated module
|
||||
==========================
|
||||
|
||||
.. automodule:: pype.lib.deprecated
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.lib.editorial.rst
Normal file
7
docs/source/pype.lib.editorial.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.editorial module
|
||||
=========================
|
||||
|
||||
.. automodule:: pype.lib.editorial
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.lib.env_tools.rst
Normal file
7
docs/source/pype.lib.env_tools.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.env\_tools module
|
||||
==========================
|
||||
|
||||
.. automodule:: pype.lib.env_tools
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.lib.ffmpeg_utils.rst
Normal file
7
docs/source/pype.lib.ffmpeg_utils.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.ffmpeg\_utils module
|
||||
=============================
|
||||
|
||||
.. automodule:: pype.lib.ffmpeg_utils
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.lib.path_tools.rst
Normal file
7
docs/source/pype.lib.path_tools.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.path\_tools module
|
||||
===========================
|
||||
|
||||
.. automodule:: pype.lib.path_tools
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.lib.plugin_tools.rst
Normal file
7
docs/source/pype.lib.plugin_tools.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.plugin\_tools module
|
||||
=============================
|
||||
|
||||
.. automodule:: pype.lib.plugin_tools
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.lib.python_module_tools.rst
Normal file
7
docs/source/pype.lib.python_module_tools.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.python\_module\_tools module
|
||||
=====================================
|
||||
|
||||
.. automodule:: pype.lib.python_module_tools
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.lib.terminal_splash.rst
Normal file
7
docs/source/pype.lib.terminal_splash.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.lib.terminal\_splash module
|
||||
================================
|
||||
|
||||
.. automodule:: pype.lib.terminal_splash
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.modules.clockify.clockify_module.rst
Normal file
7
docs/source/pype.modules.clockify.clockify_module.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.clockify.clockify\_module module
|
||||
=============================================
|
||||
|
||||
.. automodule:: pype.modules.clockify.clockify_module
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.modules.deadline.deadline_module.rst
Normal file
7
docs/source/pype.modules.deadline.deadline_module.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.deadline.deadline\_module module
|
||||
=============================================
|
||||
|
||||
.. automodule:: pype.modules.deadline.deadline_module
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
15
docs/source/pype.modules.deadline.rst
Normal file
15
docs/source/pype.modules.deadline.rst
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
pype.modules.deadline package
|
||||
=============================
|
||||
|
||||
.. automodule:: pype.modules.deadline
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 10
|
||||
|
||||
pype.modules.deadline.deadline_module
|
||||
7
docs/source/pype.modules.ftrack.ftrack_module.rst
Normal file
7
docs/source/pype.modules.ftrack.ftrack_module.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.ftrack.ftrack\_module module
|
||||
=========================================
|
||||
|
||||
.. automodule:: pype.modules.ftrack.ftrack_module
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.modules.ftrack.lib.settings.rst
Normal file
7
docs/source/pype.modules.ftrack.lib.settings.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.ftrack.lib.settings module
|
||||
=======================================
|
||||
|
||||
.. automodule:: pype.modules.ftrack.lib.settings
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.modules.ftrack.tray.ftrack_tray.rst
Normal file
7
docs/source/pype.modules.ftrack.tray.ftrack_tray.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.ftrack.tray.ftrack\_tray module
|
||||
============================================
|
||||
|
||||
.. automodule:: pype.modules.ftrack.tray.ftrack_tray
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.modules.launcher_action.rst
Normal file
7
docs/source/pype.modules.launcher_action.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.launcher\_action module
|
||||
====================================
|
||||
|
||||
.. automodule:: pype.modules.launcher_action
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.modules.log_viewer.log_view_module.rst
Normal file
7
docs/source/pype.modules.log_viewer.log_view_module.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.log\_viewer.log\_view\_module module
|
||||
=================================================
|
||||
|
||||
.. automodule:: pype.modules.log_viewer.log_view_module
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
23
docs/source/pype.modules.log_viewer.rst
Normal file
23
docs/source/pype.modules.log_viewer.rst
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
pype.modules.log\_viewer package
|
||||
================================
|
||||
|
||||
.. automodule:: pype.modules.log_viewer
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Subpackages
|
||||
-----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 10
|
||||
|
||||
pype.modules.log_viewer.tray
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 10
|
||||
|
||||
pype.modules.log_viewer.log_view_module
|
||||
7
docs/source/pype.modules.log_viewer.tray.app.rst
Normal file
7
docs/source/pype.modules.log_viewer.tray.app.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.log\_viewer.tray.app module
|
||||
========================================
|
||||
|
||||
.. automodule:: pype.modules.log_viewer.tray.app
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.modules.log_viewer.tray.models.rst
Normal file
7
docs/source/pype.modules.log_viewer.tray.models.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.log\_viewer.tray.models module
|
||||
===========================================
|
||||
|
||||
.. automodule:: pype.modules.log_viewer.tray.models
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
17
docs/source/pype.modules.log_viewer.tray.rst
Normal file
17
docs/source/pype.modules.log_viewer.tray.rst
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
pype.modules.log\_viewer.tray package
|
||||
=====================================
|
||||
|
||||
.. automodule:: pype.modules.log_viewer.tray
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 10
|
||||
|
||||
pype.modules.log_viewer.tray.app
|
||||
pype.modules.log_viewer.tray.models
|
||||
pype.modules.log_viewer.tray.widgets
|
||||
7
docs/source/pype.modules.log_viewer.tray.widgets.rst
Normal file
7
docs/source/pype.modules.log_viewer.tray.widgets.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.log\_viewer.tray.widgets module
|
||||
============================================
|
||||
|
||||
.. automodule:: pype.modules.log_viewer.tray.widgets
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.modules.settings_action.rst
Normal file
7
docs/source/pype.modules.settings_action.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.settings\_action module
|
||||
====================================
|
||||
|
||||
.. automodule:: pype.modules.settings_action
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.modules.standalonepublish_action.rst
Normal file
7
docs/source/pype.modules.standalonepublish_action.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.standalonepublish\_action module
|
||||
=============================================
|
||||
|
||||
.. automodule:: pype.modules.standalonepublish_action
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
16
docs/source/pype.modules.sync_server.rst
Normal file
16
docs/source/pype.modules.sync_server.rst
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
pype.modules.sync\_server package
|
||||
=================================
|
||||
|
||||
.. automodule:: pype.modules.sync_server
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 10
|
||||
|
||||
pype.modules.sync_server.sync_server
|
||||
pype.modules.sync_server.utils
|
||||
7
docs/source/pype.modules.sync_server.sync_server.rst
Normal file
7
docs/source/pype.modules.sync_server.sync_server.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.sync\_server.sync\_server module
|
||||
=============================================
|
||||
|
||||
.. automodule:: pype.modules.sync_server.sync_server
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.modules.sync_server.utils.rst
Normal file
7
docs/source/pype.modules.sync_server.utils.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.sync\_server.utils module
|
||||
======================================
|
||||
|
||||
.. automodule:: pype.modules.sync_server.utils
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
pype.modules.websocket\_server.hosts.aftereffects module
|
||||
========================================================
|
||||
|
||||
.. automodule:: pype.modules.websocket_server.hosts.aftereffects
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
pype.plugins.maya.publish.validate\_vray\_referenced\_aovs module
|
||||
=================================================================
|
||||
|
||||
.. automodule:: pype.plugins.maya.publish.validate_vray_referenced_aovs
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.settings.constants.rst
Normal file
7
docs/source/pype.settings.constants.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.settings.constants module
|
||||
==============================
|
||||
|
||||
.. automodule:: pype.settings.constants
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.settings.handlers.rst
Normal file
7
docs/source/pype.settings.handlers.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.settings.handlers module
|
||||
=============================
|
||||
|
||||
.. automodule:: pype.settings.handlers
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.tests.test_lib_restructuralization.rst
Normal file
7
docs/source/pype.tests.test_lib_restructuralization.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.tests.test\_lib\_restructuralization module
|
||||
================================================
|
||||
|
||||
.. automodule:: pype.tests.test_lib_restructuralization
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.tools.tray.pype_tray.rst
Normal file
7
docs/source/pype.tools.tray.pype_tray.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.tools.tray.pype\_tray module
|
||||
=================================
|
||||
|
||||
.. automodule:: pype.tools.tray.pype_tray
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
15
docs/source/pype.tools.tray.rst
Normal file
15
docs/source/pype.tools.tray.rst
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
pype.tools.tray package
|
||||
=======================
|
||||
|
||||
.. automodule:: pype.tools.tray
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 10
|
||||
|
||||
pype.tools.tray.pype_tray
|
||||
7
docs/source/pype.tools.workfiles.app.rst
Normal file
7
docs/source/pype.tools.workfiles.app.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.tools.workfiles.app module
|
||||
===============================
|
||||
|
||||
.. automodule:: pype.tools.workfiles.app
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
7
docs/source/pype.tools.workfiles.model.rst
Normal file
7
docs/source/pype.tools.workfiles.model.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.tools.workfiles.model module
|
||||
=================================
|
||||
|
||||
.. automodule:: pype.tools.workfiles.model
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
17
docs/source/pype.tools.workfiles.rst
Normal file
17
docs/source/pype.tools.workfiles.rst
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
pype.tools.workfiles package
|
||||
============================
|
||||
|
||||
.. automodule:: pype.tools.workfiles
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 10
|
||||
|
||||
pype.tools.workfiles.app
|
||||
pype.tools.workfiles.model
|
||||
pype.tools.workfiles.view
|
||||
7
docs/source/pype.tools.workfiles.view.rst
Normal file
7
docs/source/pype.tools.workfiles.view.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pype.tools.workfiles.view module
|
||||
================================
|
||||
|
||||
.. automodule:: pype.tools.workfiles.view
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
|
@ -9,8 +9,10 @@ from .bootstrap_repos import BootstrapRepos
|
|||
|
||||
|
||||
def run():
|
||||
"""Show Igniter dialog."""
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
d = InstallDialog()
|
||||
d.exec_()
|
||||
d.show()
|
||||
sys.exit(app.exec_())
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Bootstrap Pype repositories."""
|
||||
import sys
|
||||
import functools
|
||||
import logging as log
|
||||
import os
|
||||
import re
|
||||
import logging as log
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
from typing import Union, Callable, List
|
||||
from zipfile import ZipFile
|
||||
from pathlib import Path
|
||||
import functools
|
||||
|
||||
from speedcopy import copyfile
|
||||
from typing import Union, Callable, List, Tuple
|
||||
from zipfile import ZipFile, BadZipFile
|
||||
|
||||
from appdirs import user_data_dir
|
||||
from pype.version import __version__
|
||||
from pype.lib import PypeSettingsRegistry
|
||||
from speedcopy import copyfile
|
||||
|
||||
from .user_settings import PypeSettingsRegistry
|
||||
from .tools import load_environments
|
||||
|
||||
|
||||
|
|
@ -24,21 +23,24 @@ class PypeVersion:
|
|||
"""Class for storing information about Pype version.
|
||||
|
||||
Attributes:
|
||||
major (int): [1].2.3-variant-client
|
||||
minor (int): 1.[2].3-variant-client
|
||||
subversion (int): 1.2.[3]-variant-client
|
||||
variant (str): 1.2.3-[variant]-client
|
||||
client (str): 1.2.3-variant-[client]
|
||||
major (int): [1].2.3-client-variant
|
||||
minor (int): 1.[2].3-client-variant
|
||||
subversion (int): 1.2.[3]-client-variant
|
||||
client (str): 1.2.3-[client]-variant
|
||||
variant (str): 1.2.3-client-[variant]
|
||||
path (str): path to Pype
|
||||
|
||||
"""
|
||||
major = 0
|
||||
minor = 0
|
||||
subversion = 0
|
||||
variant = "production"
|
||||
variant = ""
|
||||
client = None
|
||||
path = None
|
||||
|
||||
_version_regex = re.compile(
|
||||
r"(?P<major>\d+)\.(?P<minor>\d+)\.(?P<sub>\d+)(-(?P<var1>staging)|-(?P<client>.+)(-(?P<var2>staging)))?") # noqa: E501
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
"""return formatted version string."""
|
||||
|
|
@ -55,15 +57,14 @@ class PypeVersion:
|
|||
|
||||
def __init__(self, major: int = None, minor: int = None,
|
||||
subversion: int = None, version: str = None,
|
||||
variant: str = "production", client: str = None,
|
||||
variant: str = "", client: str = None,
|
||||
path: Path = None):
|
||||
self.path = path
|
||||
self._version_regex = re.compile(
|
||||
r"(?P<major>\d+)\.(?P<minor>\d+)\.(?P<sub>\d+)(-?((?P<variant>staging)|(?P<client>.+))(-(?P<cli>.+))?)?") # noqa: E501
|
||||
|
||||
if major is None or minor is None or subversion is None:
|
||||
if version is None:
|
||||
raise ValueError("Need version specified in some way.")
|
||||
if (
|
||||
major is None or minor is None or subversion is None
|
||||
) and version is None:
|
||||
raise ValueError("Need version specified in some way.")
|
||||
if version:
|
||||
values = self._decompose_version(version)
|
||||
self.major = values[0]
|
||||
|
|
@ -83,25 +84,27 @@ class PypeVersion:
|
|||
|
||||
def _compose_version(self):
|
||||
version = "{}.{}.{}".format(self.major, self.minor, self.subversion)
|
||||
if self.variant == "staging":
|
||||
version = "{}-{}".format(version, self.variant)
|
||||
|
||||
if self.client:
|
||||
version = "{}-{}".format(version, self.client)
|
||||
|
||||
if self.variant == "staging":
|
||||
version = "{}-{}".format(version, self.variant)
|
||||
|
||||
return version
|
||||
|
||||
def _decompose_version(self, version_string: str) -> tuple:
|
||||
m = re.match(self._version_regex, version_string)
|
||||
@classmethod
|
||||
def _decompose_version(cls, version_string: str) -> tuple:
|
||||
m = re.search(cls._version_regex, version_string)
|
||||
if not m:
|
||||
raise ValueError(
|
||||
"Cannot parse version string: {}".format(version_string))
|
||||
|
||||
variant = None
|
||||
if m.group("variant") == "staging":
|
||||
if m.group("var1") == "staging" or m.group("var2") == "staging":
|
||||
variant = "staging"
|
||||
|
||||
client = m.group("client") or m.group("cli")
|
||||
client = m.group("client")
|
||||
|
||||
return (int(m.group("major")), int(m.group("minor")),
|
||||
int(m.group("sub")), variant, client)
|
||||
|
|
@ -122,21 +125,85 @@ class PypeVersion:
|
|||
return hash(self.version)
|
||||
|
||||
def __lt__(self, other):
|
||||
if self.major < other.major:
|
||||
if (self.major, self.minor, self.subversion) < \
|
||||
(other.major, other.minor, other.subversion):
|
||||
return True
|
||||
|
||||
if self.major <= other.major and self.minor < other.minor:
|
||||
return True
|
||||
if self.major <= other.major and self.minor <= other.minor and \
|
||||
self.subversion < other.subversion:
|
||||
# 1.2.3-staging < 1.2.3-client-staging
|
||||
if self.get_main_version() == other.get_main_version() and \
|
||||
not self.client and self.variant and \
|
||||
other.client and other.variant:
|
||||
return True
|
||||
|
||||
if self.major == other.major and self.minor == other.minor and \
|
||||
self.subversion == other.subversion and \
|
||||
self.variant == "staging":
|
||||
# 1.2.3 < 1.2.3-staging
|
||||
if self.get_main_version() == other.get_main_version() and \
|
||||
not self.client and self.variant and \
|
||||
not other.client and not other.variant:
|
||||
return True
|
||||
|
||||
return False
|
||||
# 1.2.3 < 1.2.3-client
|
||||
if self.get_main_version() == other.get_main_version() and \
|
||||
not self.client and not self.variant and \
|
||||
other.client and not other.variant:
|
||||
return True
|
||||
|
||||
# 1.2.3 < 1.2.3-client-staging
|
||||
if self.get_main_version() == other.get_main_version() and \
|
||||
not self.client and not self.variant and other.client:
|
||||
return True
|
||||
|
||||
# 1.2.3-client-staging < 1.2.3-client
|
||||
if self.get_main_version() == other.get_main_version() and \
|
||||
self.client and self.variant and \
|
||||
other.client and not other.variant:
|
||||
return True
|
||||
|
||||
# prefer path over no path
|
||||
if self.version == other.version and \
|
||||
not self.path and other.path:
|
||||
return True
|
||||
|
||||
# prefer path with dir over path with file
|
||||
return self.version == other.version and self.path and \
|
||||
other.path and self.path.is_file() and \
|
||||
other.path.is_dir()
|
||||
|
||||
def is_staging(self) -> bool:
|
||||
"""Test if current version is staging one."""
|
||||
return self.variant == "staging"
|
||||
|
||||
def get_main_version(self) -> str:
|
||||
"""Return main version component.
|
||||
|
||||
This returns x.x.x part of version from possibly more complex one
|
||||
like x.x.x-foo-bar.
|
||||
|
||||
Returns:
|
||||
str: main version component
|
||||
|
||||
"""
|
||||
return "{}.{}.{}".format(self.major, self.minor, self.subversion)
|
||||
|
||||
@staticmethod
|
||||
def version_in_str(string: str) -> Tuple:
|
||||
"""Find Pype version in given string.
|
||||
|
||||
Args:
|
||||
string (str): string to search.
|
||||
|
||||
Returns:
|
||||
tuple: True/False and PypeVersion if found.
|
||||
|
||||
"""
|
||||
try:
|
||||
result = PypeVersion._decompose_version(string)
|
||||
except ValueError:
|
||||
return False, None
|
||||
return True, PypeVersion(major=result[0],
|
||||
minor=result[1],
|
||||
subversion=result[2],
|
||||
variant=result[3],
|
||||
client=result[4])
|
||||
|
||||
|
||||
class BootstrapRepos:
|
||||
|
|
@ -146,15 +213,20 @@ class BootstrapRepos:
|
|||
data_dir (Path): local Pype installation directory.
|
||||
live_repo_dir (Path): path to repos directory if running live,
|
||||
otherwise `None`.
|
||||
registry (PypeSettingsRegistry): Pype registry object.
|
||||
zip_filter (list): List of files to exclude from zip
|
||||
pype_filter (list): list of top level directories not to include in
|
||||
zip in Pype repository.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, progress_callback: Callable = None):
|
||||
def __init__(self, progress_callback: Callable = None, message=None):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
progress_callback (callable): Optional callback method to report
|
||||
progress.
|
||||
message (QtCore.Signal, optional): Signal to report messages back.
|
||||
|
||||
"""
|
||||
# vendor and app used to construct user data dir
|
||||
|
|
@ -163,9 +235,15 @@ class BootstrapRepos:
|
|||
self._log = log.getLogger(str(__class__))
|
||||
self.data_dir = Path(user_data_dir(self._app, self._vendor))
|
||||
self.registry = PypeSettingsRegistry()
|
||||
self.zip_filter = [".pyc", "__pycache__"]
|
||||
self.pype_filter = [
|
||||
"build", "docs", "tests", "repos", "tools", "venv"
|
||||
]
|
||||
self._message = message
|
||||
|
||||
# dummy progress reporter
|
||||
def empty_progress(x: int):
|
||||
"""Progress callback dummy."""
|
||||
return x
|
||||
|
||||
if not progress_callback:
|
||||
|
|
@ -194,9 +272,14 @@ class BootstrapRepos:
|
|||
return v.path
|
||||
|
||||
@staticmethod
|
||||
def get_local_version() -> str:
|
||||
def get_local_live_version() -> str:
|
||||
"""Get version of local Pype."""
|
||||
return __version__
|
||||
|
||||
version = {}
|
||||
path = Path(os.path.dirname(__file__)).parent / "pype" / "version.py"
|
||||
with open(path, "r") as fp:
|
||||
exec(fp.read(), version)
|
||||
return version["__version__"]
|
||||
|
||||
@staticmethod
|
||||
def get_version(repo_dir: Path) -> Union[str, None]:
|
||||
|
|
@ -225,7 +308,7 @@ class BootstrapRepos:
|
|||
"""Copy zip created from Pype repositories to user data dir.
|
||||
|
||||
This detect Pype version either in local "live" Pype repository
|
||||
or in user provided path. Then it will zip in in temporary directory
|
||||
or in user provided path. Then it will zip it in temporary directory
|
||||
and finally it will move it to destination which is user data
|
||||
directory. Existing files will be replaced.
|
||||
|
||||
|
|
@ -240,7 +323,7 @@ class BootstrapRepos:
|
|||
# version and use it as a source. Otherwise repo_dir is user
|
||||
# entered location.
|
||||
if not repo_dir:
|
||||
version = self.get_local_version()
|
||||
version = self.get_local_live_version()
|
||||
repo_dir = self.live_repo_dir
|
||||
else:
|
||||
version = self.get_version(repo_dir)
|
||||
|
|
@ -252,7 +335,7 @@ class BootstrapRepos:
|
|||
# create zip inside temporary directory.
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
temp_zip = \
|
||||
Path(temp_dir) / f"pype-repositories-v{version}.zip"
|
||||
Path(temp_dir) / f"pype-v{version}.zip"
|
||||
self._log.info(f"creating zip: {temp_zip}")
|
||||
|
||||
self._create_pype_zip(temp_zip, repo_dir)
|
||||
|
|
@ -275,7 +358,7 @@ class BootstrapRepos:
|
|||
except shutil.Error as e:
|
||||
self._log.error(e)
|
||||
return None
|
||||
return self.data_dir / temp_zip.name
|
||||
return destination
|
||||
|
||||
def _create_pype_zip(
|
||||
self,
|
||||
|
|
@ -284,11 +367,9 @@ class BootstrapRepos:
|
|||
"""Pack repositories and Pype into zip.
|
||||
|
||||
We are using :mod:`zipfile` instead :meth:`shutil.make_archive`
|
||||
to later implement file filter to skip git related stuff to make
|
||||
it into archive.
|
||||
|
||||
Todo:
|
||||
Implement file filter
|
||||
because we need to decide what file and directories to include in zip
|
||||
and what not. They are determined by :attr:`zip_filter` on file level
|
||||
and :attr:`pype_filter` on top level directory in Pype repository.
|
||||
|
||||
Args:
|
||||
zip_path (str): path to zip file.
|
||||
|
|
@ -296,42 +377,82 @@ class BootstrapRepos:
|
|||
include_pype (bool): add Pype module itself.
|
||||
|
||||
"""
|
||||
repo_files = sum(len(files) for _, _, files in os.walk(include_dir))
|
||||
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)
|
||||
# count them
|
||||
repo_files = len(repo_list)
|
||||
|
||||
# there must be some files, otherwise `include_dir` path is wrong
|
||||
assert repo_files != 0, f"No repositories to include in {include_dir}"
|
||||
pype_inc = 0
|
||||
if include_pype:
|
||||
pype_files = sum(len(files) for _, _, files in os.walk(
|
||||
include_dir.parent))
|
||||
# get filtered list of file in Pype repository
|
||||
pype_list = _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:
|
||||
for root, _, files in os.walk(include_dir.as_posix()):
|
||||
for file in files:
|
||||
zip_file.write(
|
||||
os.path.relpath(os.path.join(root, file),
|
||||
os.path.join(include_dir, '..')),
|
||||
os.path.relpath(os.path.join(root, file),
|
||||
os.path.join(include_dir))
|
||||
)
|
||||
progress += repo_inc
|
||||
self._progress_callback(int(progress))
|
||||
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(include_dir)
|
||||
zip_file.write(file, arc_name)
|
||||
|
||||
# add pype itself
|
||||
if include_pype:
|
||||
for root, _, files in os.walk("pype"):
|
||||
for file in files:
|
||||
zip_file.write(
|
||||
os.path.relpath(os.path.join(root, file),
|
||||
os.path.join('pype', '..')),
|
||||
os.path.join(
|
||||
'pype',
|
||||
os.path.relpath(os.path.join(root, file),
|
||||
os.path.join('pype', '..')))
|
||||
)
|
||||
progress += pype_inc
|
||||
self._progress_callback(int(progress))
|
||||
pype_root = include_dir.parent.resolve()
|
||||
# generate list of filtered paths
|
||||
dir_filter = [pype_root / f for f in self.pype_filter]
|
||||
|
||||
file: Path
|
||||
for file in pype_list:
|
||||
progress += pype_inc
|
||||
self._progress_callback(int(progress))
|
||||
|
||||
# if file resides in filtered path, skip it
|
||||
is_inside = None
|
||||
df: Path
|
||||
for df in dir_filter:
|
||||
try:
|
||||
is_inside = file.resolve().relative_to(df)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if is_inside:
|
||||
continue
|
||||
|
||||
processed_path = file
|
||||
self._log.debug(f"processing {processed_path}")
|
||||
self._print(f"- processing {processed_path}", False)
|
||||
|
||||
zip_file.write(file,
|
||||
"pype" / file.relative_to(pype_root))
|
||||
|
||||
# test if zip is ok
|
||||
zip_file.testzip()
|
||||
self._progress_callback(100)
|
||||
|
||||
|
|
@ -342,10 +463,15 @@ class BootstrapRepos:
|
|||
This will enable Python to import modules is second-level directories
|
||||
in zip file.
|
||||
|
||||
Adding to both `sys.path` and `PYTHONPATH`, skipping duplicates.
|
||||
|
||||
Args:
|
||||
archive (str): path to archive.
|
||||
archive (Path): path to archive.
|
||||
|
||||
"""
|
||||
if not archive.is_file() and not archive.exists():
|
||||
raise ValueError("Archive is not file.")
|
||||
|
||||
with ZipFile(archive, "r") as zip_file:
|
||||
name_list = zip_file.namelist()
|
||||
|
||||
|
|
@ -362,8 +488,41 @@ class BootstrapRepos:
|
|||
|
||||
os.environ["PYTHONPATH"] = os.pathsep.join(paths)
|
||||
|
||||
@staticmethod
|
||||
def add_paths_from_directory(directory: Path) -> None:
|
||||
"""Add first level directories as paths to :mod:`sys.path`.
|
||||
|
||||
This works the same as :meth:`add_paths_from_archive` but in
|
||||
specified directory.
|
||||
|
||||
Adding to both `sys.path` and `PYTHONPATH`, skipping duplicates.
|
||||
|
||||
Args:
|
||||
directory (Path): path to directory.
|
||||
|
||||
"""
|
||||
if not directory.exists() and not directory.is_dir():
|
||||
raise ValueError("directory is invalid")
|
||||
|
||||
roots = []
|
||||
for item in directory.iterdir():
|
||||
if item.is_dir():
|
||||
root = item.as_posix()
|
||||
if root not in roots:
|
||||
roots.append(root)
|
||||
sys.path.insert(0, root)
|
||||
|
||||
pythonpath = os.getenv("PYTHONPATH", "")
|
||||
paths = pythonpath.split(os.pathsep)
|
||||
paths += roots
|
||||
|
||||
os.environ["PYTHONPATH"] = os.pathsep.join(paths)
|
||||
|
||||
def find_pype(
|
||||
self, pype_path: Path = None) -> Union[List[PypeVersion], None]:
|
||||
self,
|
||||
pype_path: Path = None,
|
||||
staging: bool = False,
|
||||
include_zips: bool = False) -> Union[List[PypeVersion], None]:
|
||||
"""Get ordered dict of detected Pype version.
|
||||
|
||||
Resolution order for Pype is following:
|
||||
|
|
@ -374,6 +533,10 @@ class BootstrapRepos:
|
|||
|
||||
Args:
|
||||
pype_path (Path, optional): Try to find Pype on the given path.
|
||||
staging (bool, optional): Filter only staging version, skip them
|
||||
otherwise.
|
||||
include_zips (bool, optional): If set True it will try to find
|
||||
Pype in zip files in given directory.
|
||||
|
||||
Returns:
|
||||
dict of Path: Dictionary of detected Pype version.
|
||||
|
|
@ -383,42 +546,110 @@ class BootstrapRepos:
|
|||
|
||||
"""
|
||||
dir_to_search = self.data_dir
|
||||
if os.getenv("PYPE_PATH"):
|
||||
if Path(os.getenv("PYPE_PATH")).exists():
|
||||
dir_to_search = Path(os.getenv("PYPE_PATH"))
|
||||
else:
|
||||
try:
|
||||
registry_dir = Path(self.registry.get_item("pypePath"))
|
||||
if registry_dir.exists():
|
||||
dir_to_search = registry_dir
|
||||
|
||||
except ValueError:
|
||||
# nothing found in registry, we'll use data dir
|
||||
pass
|
||||
|
||||
# if we have pyp_path specified, search only there.
|
||||
# if we have pype_path specified, search only there.
|
||||
if pype_path:
|
||||
dir_to_search = pype_path
|
||||
else:
|
||||
if os.getenv("PYPE_PATH"):
|
||||
if Path(os.getenv("PYPE_PATH")).exists():
|
||||
dir_to_search = Path(os.getenv("PYPE_PATH"))
|
||||
else:
|
||||
try:
|
||||
registry_dir = Path(
|
||||
str(self.registry.get_item("pypePath")))
|
||||
if registry_dir.exists():
|
||||
dir_to_search = registry_dir
|
||||
|
||||
except ValueError:
|
||||
# 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 = []
|
||||
file_pattern = re.compile(r"^pype-repositories-v(?P<version>\d+\.\d+\.\d*.+?).zip$") # noqa: E501
|
||||
# iterate over directory in first level and find all that might
|
||||
# contain Pype.
|
||||
for file in dir_to_search.iterdir():
|
||||
m = re.match(
|
||||
file_pattern,
|
||||
file.name)
|
||||
if m:
|
||||
try:
|
||||
_pype_versions.append(
|
||||
PypeVersion(
|
||||
version=m.group("version"), path=file))
|
||||
except ValueError:
|
||||
# cannot parse version string
|
||||
print(m)
|
||||
pass
|
||||
|
||||
# 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)
|
||||
|
||||
|
|
@ -426,16 +657,16 @@ class BootstrapRepos:
|
|||
def _get_pype_from_mongo(mongo_url: str) -> Union[Path, None]:
|
||||
"""Get path from Mongo database.
|
||||
|
||||
This sets environment variable ``AVALON_MONGO`` for
|
||||
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_ROOT``.
|
||||
must be ``PYPE_PATH``.
|
||||
|
||||
Args:
|
||||
mongo_url (str): mongodb connection url
|
||||
|
||||
Returns:
|
||||
Path: if path from ``PYPE_ROOT`` is found.
|
||||
Path: if path from ``PYPE_PATH`` is found.
|
||||
None: if not.
|
||||
|
||||
"""
|
||||
|
|
@ -479,11 +710,18 @@ class BootstrapRepos:
|
|||
self._log.error(f"{pype_path} doesn't exists.")
|
||||
return None
|
||||
|
||||
# find pype zip files in location. In that location, there can be
|
||||
# either "live" Pype repository, or multiple zip files.
|
||||
# test if entered path isn't user data dir
|
||||
if self.data_dir == pype_path:
|
||||
self._log.error("cannot point to user data dir")
|
||||
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)
|
||||
if versions:
|
||||
self._log.info(f"found Pype zips in [ {pype_path} ].")
|
||||
self._log.info(f"found Pype in [ {pype_path} ]")
|
||||
self._log.info(f"latest version found is [ {versions[-1]} ]")
|
||||
|
||||
destination = self.data_dir / versions[-1].path.name
|
||||
|
|
@ -503,13 +741,43 @@ class BootstrapRepos:
|
|||
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
|
||||
|
||||
# if we got here, it means that location is "live" Pype repository.
|
||||
|
|
@ -518,4 +786,171 @@ class BootstrapRepos:
|
|||
if not repo_file.exists():
|
||||
self._log.error(f"installing zip {repo_file} failed.")
|
||||
return None
|
||||
return repo_file
|
||||
|
||||
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
|
||||
|
||||
destination.mkdir(parents=True)
|
||||
|
||||
# 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
|
||||
|
||||
def _print(self, message, error=False):
|
||||
if self._message:
|
||||
self._message.emit(message, error)
|
||||
|
||||
def extract_pype(self, version: PypeVersion) -> Union[Path, None]:
|
||||
"""Extract zipped Pype version to user data directory.
|
||||
|
||||
Args:
|
||||
version (PypeVersion): Version of Pype.
|
||||
|
||||
Returns:
|
||||
Path: path to extracted version.
|
||||
None: if something failed.
|
||||
|
||||
"""
|
||||
if not version.path:
|
||||
raise ValueError(
|
||||
f"version {version} is not associated with any file")
|
||||
|
||||
destination = self.data_dir / version.path.stem
|
||||
if destination.exists():
|
||||
try:
|
||||
destination.unlink()
|
||||
except OSError as e:
|
||||
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)
|
||||
return None
|
||||
|
||||
destination.mkdir(parents=True)
|
||||
|
||||
# extract zip there
|
||||
self._print("Extracting zip to destination ...")
|
||||
with ZipFile(version.path, "r") as zip_ref:
|
||||
zip_ref.extractall(destination)
|
||||
|
||||
self._print(f"Installed as {version.path.stem}")
|
||||
|
||||
return destination
|
||||
|
||||
def install_version(self, pype_version: PypeVersion, force: bool = False):
|
||||
"""Install Pype version to user data directory.
|
||||
|
||||
Args:
|
||||
pype_version (PypeVersion): Pype version to install.
|
||||
force (bool, optional): Force overwrite existing version.
|
||||
|
||||
Returns:
|
||||
Path: Path to installed Pype.
|
||||
|
||||
Raises:
|
||||
PypeVersionExists: If not forced and this version already exist
|
||||
in user data directory.
|
||||
PypeVersionInvalid: If version to install is invalid.
|
||||
PypeVersionIOError: If copying or zipping fail.
|
||||
|
||||
"""
|
||||
|
||||
# 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:
|
||||
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
|
||||
|
||||
# test if destination file already exist, if so lets delete it.
|
||||
# we consider path on location as authoritative place.
|
||||
if destination.exists() and force:
|
||||
try:
|
||||
destination.unlink()
|
||||
except OSError:
|
||||
self._log.error(
|
||||
f"cannot remove already existing {destination}",
|
||||
exc_info=True)
|
||||
return None
|
||||
else:
|
||||
raise PypeVersionExists(f"{destination} already exist.")
|
||||
|
||||
# create destination parent directories even if they don't exist.
|
||||
if not destination.exists():
|
||||
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 ...")
|
||||
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._create_pype_zip(temp_zip, pype_version.path)
|
||||
if not os.path.exists(temp_zip):
|
||||
self._log.error("make archive failed.")
|
||||
raise PypeVersionIOError("Zip creation failed.")
|
||||
|
||||
# set zip as version source
|
||||
pype_version.path = temp_zip
|
||||
|
||||
elif pype_version.path.is_file():
|
||||
# check if file is zip (by extension)
|
||||
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
|
||||
|
||||
# extract zip there
|
||||
self._log.info("extracting zip to destination ...")
|
||||
with ZipFile(pype_version.path, "r") as zip_ref:
|
||||
zip_ref.extractall(destination)
|
||||
|
||||
return destination
|
||||
|
||||
|
||||
class PypeVersionExists(Exception):
|
||||
"""Exception for handling existing Pype version."""
|
||||
pass
|
||||
|
||||
|
||||
class PypeVersionInvalid(Exception):
|
||||
"""Exception for handling invalid Pype version."""
|
||||
pass
|
||||
|
||||
|
||||
class PypeVersionIOError(Exception):
|
||||
"""Exception for handling IO errors in Pype version."""
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
def __init__(self, parent=None):
|
||||
super(InstallDialog, self).__init__(parent)
|
||||
|
||||
self._mongo_url = ""
|
||||
self._mongo_url = os.getenv("PYPE_MONGO", "")
|
||||
|
||||
self.setWindowTitle("Pype - Configure Pype repository path")
|
||||
self._icon_path = os.path.join(
|
||||
|
|
@ -149,10 +149,13 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
self.setLayout(mongo_layout)
|
||||
|
||||
def _mongo_changed(self, mongo: str):
|
||||
self.parent()._mongo_url = mongo
|
||||
self.parent().mongo_url = mongo
|
||||
|
||||
def get_mongo_url(self):
|
||||
return self.parent()._mongo_url
|
||||
return self.parent().mongo_url
|
||||
|
||||
def set_mongo_url(self, mongo: str):
|
||||
self._mongo_input.setText(mongo)
|
||||
|
||||
def set_valid(self):
|
||||
self._mongo_input.setStyleSheet(
|
||||
|
|
@ -175,6 +178,8 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
)
|
||||
|
||||
self._mongo = MongoWidget(self)
|
||||
if self._mongo_url:
|
||||
self._mongo.set_mongo_url(self._mongo_url)
|
||||
|
||||
# Bottom button bar
|
||||
# --------------------------------------------------------------------
|
||||
|
|
@ -303,14 +308,17 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
options |= QtWidgets.QFileDialog.DontUseNativeDialog
|
||||
options |= QtWidgets.QFileDialog.ShowDirsOnly
|
||||
|
||||
filename, _ = QtWidgets.QFileDialog.getOpenFileName(
|
||||
result = QtWidgets.QFileDialog.getExistingDirectory(
|
||||
parent=self,
|
||||
caption='Select path',
|
||||
directory=os.getcwd(),
|
||||
options=options)
|
||||
|
||||
if filename:
|
||||
filename = QtCore.QDir.toNativeSeparators(filename)
|
||||
if not result:
|
||||
return
|
||||
|
||||
filename = result[0]
|
||||
filename = QtCore.QDir.toNativeSeparators(filename)
|
||||
|
||||
if os.path.isdir(filename):
|
||||
self.user_input.setText(filename)
|
||||
|
|
@ -378,6 +386,7 @@ class InstallDialog(QtWidgets.QDialog):
|
|||
|
||||
if len(self._path) < 1:
|
||||
self._mongo.setVisible(False)
|
||||
return path
|
||||
|
||||
def _update_console(self, msg: str, error: bool = False) -> None:
|
||||
"""Display message in console.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Working thread for installer."""
|
||||
import os
|
||||
import sys
|
||||
from zipfile import ZipFile
|
||||
|
||||
from Qt.QtCore import QThread, Signal
|
||||
|
||||
from .bootstrap_repos import BootstrapRepos
|
||||
from .bootstrap_repos import PypeVersion
|
||||
from .tools import validate_mongo_connection
|
||||
|
||||
|
||||
|
|
@ -43,8 +46,9 @@ class InstallThread(QThread):
|
|||
self.message.emit("Installing Pype ...", False)
|
||||
|
||||
# find local version of Pype
|
||||
bs = BootstrapRepos(progress_callback=self.set_progress)
|
||||
local_version = bs.get_local_version()
|
||||
bs = BootstrapRepos(
|
||||
progress_callback=self.set_progress, message=self.message)
|
||||
local_version = bs.get_local_live_version()
|
||||
|
||||
# if user did entered nothing, we install Pype from local version.
|
||||
# zip content of `repos`, copy it to user data dir and append
|
||||
|
|
@ -68,14 +72,71 @@ class InstallThread(QThread):
|
|||
|
||||
os.environ["PYPE_MONGO"] = self._mongo
|
||||
|
||||
self.message.emit(
|
||||
f"Detecting installed Pype versions in {bs.data_dir}", False)
|
||||
detected = bs.find_pype(include_zips=True)
|
||||
|
||||
if detected:
|
||||
if PypeVersion(version=local_version) < detected[-1]:
|
||||
self.message.emit((
|
||||
f"Latest installed version {detected[-1]} is newer "
|
||||
f"then currently running {local_version}"
|
||||
), False)
|
||||
self.message.emit("Skipping Pype install ...", False)
|
||||
if detected[-1].path.suffix.lower() == ".zip":
|
||||
bs.extract_pype(detected[-1])
|
||||
return
|
||||
|
||||
if PypeVersion(version=local_version) == detected[-1]:
|
||||
self.message.emit((
|
||||
f"Latest installed version is the same as "
|
||||
f"currently running {local_version}"
|
||||
), False)
|
||||
self.message.emit("Skipping Pype install ...", False)
|
||||
return
|
||||
|
||||
self.message.emit((
|
||||
"All installed versions are older then "
|
||||
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)
|
||||
return
|
||||
else:
|
||||
self.message.emit("None detected.", False)
|
||||
|
||||
self.message.emit(
|
||||
f"We will use local Pype version {local_version}", False)
|
||||
|
||||
repo_file = bs.install_live_repos()
|
||||
if not repo_file:
|
||||
self.message.emit(
|
||||
f"!!! install failed - {repo_file}", True)
|
||||
f"!!! Install failed - {repo_file}", True)
|
||||
return
|
||||
self.message.emit(f"installed as {repo_file}", False)
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
else:
|
||||
# if we have mongo connection string, validate it, set it to
|
||||
# user settings and get PYPE_PATH from there.
|
||||
|
|
@ -87,10 +148,14 @@ class InstallThread(QThread):
|
|||
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(f"!!! Cannot install", True)
|
||||
self.message.emit("!!! Cannot install", True)
|
||||
return
|
||||
|
||||
def set_path(self, path: str) -> None:
|
||||
|
|
|
|||
BIN
igniter/pype.ico
Normal file
BIN
igniter/pype.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
413
igniter/splash.txt
Normal file
413
igniter/splash.txt
Normal file
|
|
@ -0,0 +1,413 @@
|
|||
|
||||
|
||||
|
||||
*
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.*
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*
|
||||
.*
|
||||
*
|
||||
|
||||
|
||||
|
||||
.
|
||||
*
|
||||
.*
|
||||
*
|
||||
.
|
||||
|
||||
.
|
||||
*
|
||||
.*
|
||||
.*
|
||||
.*
|
||||
*
|
||||
.
|
||||
.
|
||||
*
|
||||
.*
|
||||
.*
|
||||
.*
|
||||
*
|
||||
.
|
||||
_.
|
||||
/**
|
||||
\ *
|
||||
\*
|
||||
*
|
||||
*
|
||||
.
|
||||
__.
|
||||
---*
|
||||
\ \*
|
||||
\ *
|
||||
\*
|
||||
*
|
||||
.
|
||||
\___.
|
||||
/* *
|
||||
\ \ *
|
||||
\ \*
|
||||
\ *
|
||||
\*
|
||||
.
|
||||
|____.
|
||||
/* *
|
||||
\|\ *
|
||||
\ \ *
|
||||
\ \ *
|
||||
\ \*
|
||||
\/.
|
||||
_/_____.
|
||||
/* *
|
||||
/ \ *
|
||||
\ \ *
|
||||
\ \ *
|
||||
\ \__*
|
||||
\/__.
|
||||
__________.
|
||||
--*-- ___*
|
||||
\ \ \/_*
|
||||
\ \ __*
|
||||
\ \ \_*
|
||||
\ \____\*
|
||||
\/____/.
|
||||
\____________ .
|
||||
/* ___ \*
|
||||
\ \ \/_\ *
|
||||
\ \ _____*
|
||||
\ \ \___/*
|
||||
\ \____\ *
|
||||
\/____/ .
|
||||
|___________ .
|
||||
/* ___ \ *
|
||||
\|\ \/_\ \ *
|
||||
\ \ _____/ *
|
||||
\ \ \___/ *
|
||||
\ \____\ / *
|
||||
\/____/ \.
|
||||
_/__________ .
|
||||
/* ___ \ *
|
||||
/ \ \/_\ \ *
|
||||
\ \ _____/ *
|
||||
\ \ \___/ ---*
|
||||
\ \____\ / \__*
|
||||
\/____/ \/__.
|
||||
____________ .
|
||||
--*-- ___ \ *
|
||||
\ \ \/_\ \ *
|
||||
\ \ _____/ *
|
||||
\ \ \___/ ---- *
|
||||
\ \____\ / \____\*
|
||||
\/____/ \/____/.
|
||||
____________
|
||||
/\ ___ \ .
|
||||
\ \ \/_\ \ *
|
||||
\ \ _____/ *
|
||||
\ \ \___/ ---- *
|
||||
\ \____\ / \____\ .
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \ .
|
||||
\ \ _____/ *
|
||||
\ \ \___/ ---- *
|
||||
\ \____\ / \____\ .
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ .
|
||||
\ \ \___/ ---- *
|
||||
\ \____\ / \____\ .
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/
|
||||
\ \ \___/ ---- *
|
||||
\ \____\ / \____\
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/
|
||||
\ \ \___/ ---- .
|
||||
\ \____\ / \____\
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ _
|
||||
\ \ \___/ ----
|
||||
\ \____\ / \____\
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___
|
||||
\ \ \___/ ----
|
||||
\ \____\ / \____\
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___
|
||||
\ \ \___/ ---- \
|
||||
\ \____\ / \____\
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___
|
||||
\ \ \___/ ---- \
|
||||
\ \____\ / \____\ \
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___
|
||||
\ \ \___/ ---- \
|
||||
\ \____\ / \____\ __\
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___
|
||||
\ \ \___/ ---- \
|
||||
\ \____\ / \____\ \__\
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___
|
||||
\ \ \___/ ---- \ \
|
||||
\ \____\ / \____\ \__\
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___
|
||||
\ \ \___/ ---- \ \
|
||||
\ \____\ / \____\ \__\
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___.
|
||||
\ \ \___/ ---- \ \\
|
||||
\ \____\ / \____\ \__\,
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ .
|
||||
\ \ \___/ ---- \ \\
|
||||
\ \____\ / \____\ \__\\,
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ _.
|
||||
\ \ \___/ ---- \ \\\
|
||||
\ \____\ / \____\ \__\\\
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ __.
|
||||
\ \ \___/ ---- \ \\ \
|
||||
\ \____\ / \____\ \__\\_/.
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___.
|
||||
\ \ \___/ ---- \ \\ \\
|
||||
\ \____\ / \____\ \__\\__\.
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ .
|
||||
\ \ \___/ ---- \ \\ \\
|
||||
\ \____\ / \____\ \__\\__\\.
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ _.
|
||||
\ \ \___/ ---- \ \\ \\\
|
||||
\ \____\ / \____\ \__\\__\\.
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ __.
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\_.
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ __.
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__.
|
||||
\/____/ \/____/
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ .
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ *
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ O*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ .oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ ..oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . .oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . p.oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . Py.oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . PYp.oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . PYPe.oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . PYPE .oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . PYPE c.oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . PYPE C1.oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . PYPE ClU.oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . PYPE CluB.oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . PYPE Club .oO*
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . PYPE Club . ..
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . PYPE Club . ..
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . PYPE Club . .
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . PYPE Club .
|
||||
43
igniter/terminal_splash.py
Normal file
43
igniter/terminal_splash.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Pype terminal animation."""
|
||||
import blessed
|
||||
from pathlib import Path
|
||||
from time import sleep
|
||||
|
||||
NO_TERMINAL = False
|
||||
|
||||
try:
|
||||
term = blessed.Terminal()
|
||||
except AttributeError:
|
||||
# this happens when blessed cannot find proper terminal.
|
||||
# If so, skip printing ascii art animation.
|
||||
NO_TERMINAL = True
|
||||
|
||||
|
||||
def play_animation():
|
||||
"""Play ASCII art Pype animation."""
|
||||
if NO_TERMINAL:
|
||||
return
|
||||
print(term.home + term.clear)
|
||||
frame_size = 7
|
||||
splash_file = Path(__file__).parent / "splash.txt"
|
||||
with splash_file.open("r") as sf:
|
||||
animation = sf.readlines()
|
||||
|
||||
animation_length = int(len(animation) / frame_size)
|
||||
current_frame = 0
|
||||
for _ in range(animation_length):
|
||||
frame = "".join(
|
||||
scanline
|
||||
for y, scanline in enumerate(
|
||||
animation[current_frame : current_frame + frame_size]
|
||||
)
|
||||
)
|
||||
|
||||
with term.location(0, 0):
|
||||
# term.aquamarine3_bold(frame)
|
||||
print(f"{term.bold}{term.aquamarine3}{frame}{term.normal}")
|
||||
|
||||
sleep(0.02)
|
||||
current_frame += frame_size
|
||||
print(term.move_y(7))
|
||||
167
igniter/tools.py
167
igniter/tools.py
|
|
@ -1,14 +1,104 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Tools used in **Igniter** GUI."""
|
||||
"""Tools used in **Igniter** GUI.
|
||||
|
||||
Functions ``compose_url()`` and ``decompose_url()`` are the same as in
|
||||
``pype.lib`` and they are here to avoid importing pype module before its
|
||||
version is decided.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
from urllib.parse import urlparse
|
||||
from typing import Dict
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
|
||||
from pymongo import MongoClient
|
||||
from pymongo.errors import ServerSelectionTimeoutError, InvalidURI
|
||||
|
||||
from pype.lib import decompose_url, compose_url
|
||||
|
||||
def decompose_url(url: str) -> Dict:
|
||||
"""Decompose mongodb url to its separate components.
|
||||
|
||||
Args:
|
||||
url (str): Mongodb url.
|
||||
|
||||
Returns:
|
||||
dict: Dictionary of components.
|
||||
|
||||
"""
|
||||
components = {
|
||||
"scheme": None,
|
||||
"host": None,
|
||||
"port": None,
|
||||
"username": None,
|
||||
"password": None,
|
||||
"auth_db": None
|
||||
}
|
||||
|
||||
result = urlparse(url)
|
||||
if result.scheme is None:
|
||||
_url = "mongodb://{}".format(url)
|
||||
result = urlparse(_url)
|
||||
|
||||
components["scheme"] = result.scheme
|
||||
components["host"] = result.hostname
|
||||
try:
|
||||
components["port"] = result.port
|
||||
except ValueError:
|
||||
raise RuntimeError("invalid port specified")
|
||||
components["username"] = result.username
|
||||
components["password"] = result.password
|
||||
|
||||
try:
|
||||
components["auth_db"] = parse_qs(result.query)['authSource'][0]
|
||||
except KeyError:
|
||||
# no auth db provided, mongo will use the one we are connecting to
|
||||
pass
|
||||
|
||||
return components
|
||||
|
||||
|
||||
def compose_url(scheme: str = None,
|
||||
host: str = None,
|
||||
username: str = None,
|
||||
password: str = None,
|
||||
port: int = None,
|
||||
auth_db: str = None) -> str:
|
||||
"""Compose mongodb url from its individual components.
|
||||
|
||||
Args:
|
||||
scheme (str, optional):
|
||||
host (str, optional):
|
||||
username (str, optional):
|
||||
password (str, optional):
|
||||
port (str, optional):
|
||||
auth_db (str, optional):
|
||||
|
||||
Returns:
|
||||
str: mongodb url
|
||||
|
||||
"""
|
||||
|
||||
url = "{scheme}://"
|
||||
|
||||
if username and password:
|
||||
url += "{username}:{password}@"
|
||||
|
||||
url += "{host}"
|
||||
if port:
|
||||
url += ":{port}"
|
||||
|
||||
if auth_db:
|
||||
url += "?authSource={auth_db}"
|
||||
|
||||
return url.format(**{
|
||||
"scheme": scheme,
|
||||
"host": host,
|
||||
"username": username,
|
||||
"password": password,
|
||||
"port": port,
|
||||
"auth_db": auth_db
|
||||
})
|
||||
|
||||
|
||||
def validate_mongo_connection(cnx: str) -> (bool, str):
|
||||
|
|
@ -22,30 +112,29 @@ def validate_mongo_connection(cnx: str) -> (bool, str):
|
|||
|
||||
"""
|
||||
parsed = urlparse(cnx)
|
||||
if parsed.scheme in ["mongodb", "mongodb+srv"]:
|
||||
# we have mongo connection string. Let's try if we can connect.
|
||||
components = decompose_url(cnx)
|
||||
mongo_args = {
|
||||
"host": compose_url(**components),
|
||||
"serverSelectionTimeoutMS": 1000
|
||||
}
|
||||
port = components.get("port")
|
||||
if port is not None:
|
||||
mongo_args["port"] = int(port)
|
||||
|
||||
try:
|
||||
client = MongoClient(**mongo_args)
|
||||
client.server_info()
|
||||
except ServerSelectionTimeoutError as e:
|
||||
return False, f"Cannot connect to server {cnx} - {e}"
|
||||
except ValueError:
|
||||
return False, f"Invalid port specified {parsed.port}"
|
||||
except InvalidURI as e:
|
||||
return False, str(e)
|
||||
else:
|
||||
return True, "Connection is successful"
|
||||
else:
|
||||
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)
|
||||
mongo_args = {
|
||||
"host": compose_url(**components),
|
||||
"serverSelectionTimeoutMS": 1000
|
||||
}
|
||||
port = components.get("port")
|
||||
if port is not None:
|
||||
mongo_args["port"] = int(port)
|
||||
|
||||
try:
|
||||
client = MongoClient(**mongo_args)
|
||||
client.server_info()
|
||||
except ServerSelectionTimeoutError as e:
|
||||
return False, f"Cannot connect to server {cnx} - {e}"
|
||||
except ValueError:
|
||||
return False, f"Invalid port specified {parsed.port}"
|
||||
except InvalidURI as e:
|
||||
return False, str(e)
|
||||
else:
|
||||
return True, "Connection is successful"
|
||||
|
||||
|
||||
def validate_path_string(path: str) -> (bool, str):
|
||||
|
|
@ -81,26 +170,6 @@ def validate_path_string(path: str) -> (bool, str):
|
|||
return False, "Not implemented yet"
|
||||
|
||||
|
||||
def add_acre_to_sys_path():
|
||||
"""Add full path of acre module to sys.path on ignitation."""
|
||||
try:
|
||||
# Skip if is possible to import
|
||||
import acre
|
||||
|
||||
except ImportError:
|
||||
# Full path to acred repository related to current file
|
||||
acre_dir = os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
"repos",
|
||||
"acre"
|
||||
)
|
||||
# Add path to sys.path
|
||||
sys.path.append(acre_dir)
|
||||
|
||||
# Validate that acre can be imported
|
||||
import acre
|
||||
|
||||
|
||||
def load_environments(sections: list = None) -> dict:
|
||||
"""Load environments from Pype.
|
||||
|
||||
|
|
@ -114,7 +183,6 @@ def load_environments(sections: list = None) -> dict:
|
|||
dict of str: loaded and processed environments.
|
||||
|
||||
"""
|
||||
add_acre_to_sys_path()
|
||||
import acre
|
||||
|
||||
from pype import settings
|
||||
|
|
@ -131,5 +199,4 @@ def load_environments(sections: list = None) -> dict:
|
|||
continue
|
||||
merged_env = acre.append(merged_env, parsed_env)
|
||||
|
||||
env = acre.compute(merged_env, cleanup=True)
|
||||
return env
|
||||
return acre.compute(merged_env, cleanup=True)
|
||||
|
|
|
|||
466
igniter/user_settings.py
Normal file
466
igniter/user_settings.py
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package to deal with saving and retrieving user specific settings."""
|
||||
import os
|
||||
from datetime import datetime
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import json
|
||||
|
||||
# disable lru cache in Python 2
|
||||
try:
|
||||
from functools import lru_cache
|
||||
except ImportError:
|
||||
def lru_cache(maxsize):
|
||||
def max_size(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
value = func(*args, **kwargs)
|
||||
return value
|
||||
return wrapper
|
||||
return max_size
|
||||
|
||||
# ConfigParser was renamed in python3 to configparser
|
||||
try:
|
||||
import configparser
|
||||
except ImportError:
|
||||
import ConfigParser as configparser
|
||||
|
||||
import platform
|
||||
|
||||
import appdirs
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class ASettingRegistry():
|
||||
"""Abstract class defining structure of **SettingRegistry** class.
|
||||
|
||||
It is implementing methods to store secure items into keyring, otherwise
|
||||
mechanism for storing common items must be implemented in abstract
|
||||
methods.
|
||||
|
||||
Attributes:
|
||||
_name (str): Registry names.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
# type: (str) -> ASettingRegistry
|
||||
super(ASettingRegistry, self).__init__()
|
||||
|
||||
if six.PY3:
|
||||
import keyring
|
||||
# hack for cx_freeze and Windows keyring backend
|
||||
if platform.system() == "Windows":
|
||||
from keyring.backends import Windows
|
||||
keyring.set_keyring(Windows.WinVaultKeyring())
|
||||
|
||||
self._name = name
|
||||
self._items = {}
|
||||
|
||||
def set_item(self, name, value):
|
||||
# type: (str, str) -> None
|
||||
"""Set item to settings registry.
|
||||
|
||||
Args:
|
||||
name (str): Name of the item.
|
||||
value (str): Value of the item.
|
||||
|
||||
"""
|
||||
self._set_item(name, value)
|
||||
|
||||
@abstractmethod
|
||||
def _set_item(self, name, value):
|
||||
# type: (str, str) -> None
|
||||
# Implement it
|
||||
pass
|
||||
|
||||
def __setitem__(self, name, value):
|
||||
self._items[name] = value
|
||||
self._set_item(name, value)
|
||||
|
||||
def get_item(self, name):
|
||||
# type: (str) -> str
|
||||
"""Get item from settings registry.
|
||||
|
||||
Args:
|
||||
name (str): Name of the item.
|
||||
|
||||
Returns:
|
||||
value (str): Value of the item.
|
||||
|
||||
Raises:
|
||||
ValueError: If item doesn't exist.
|
||||
|
||||
"""
|
||||
return self._get_item(name)
|
||||
|
||||
@abstractmethod
|
||||
def _get_item(self, name):
|
||||
# type: (str) -> str
|
||||
# Implement it
|
||||
pass
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self._get_item(name)
|
||||
|
||||
def delete_item(self, name):
|
||||
# type: (str) -> None
|
||||
"""Delete item from settings registry.
|
||||
|
||||
Args:
|
||||
name (str): Name of the item.
|
||||
|
||||
"""
|
||||
self._delete_item(name)
|
||||
|
||||
@abstractmethod
|
||||
def _delete_item(self, name):
|
||||
# type: (str) -> None
|
||||
"""Delete item from settings.
|
||||
|
||||
Note:
|
||||
see :meth:`pype.lib.user_settings.ARegistrySettings.delete_item`
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def __delitem__(self, name):
|
||||
del self._items[name]
|
||||
self._delete_item(name)
|
||||
|
||||
def set_secure_item(self, name, value):
|
||||
# type: (str, str) -> None
|
||||
"""Set sensitive item into system's keyring.
|
||||
|
||||
This uses `Keyring module`_ to save sensitive stuff into system's
|
||||
keyring.
|
||||
|
||||
Args:
|
||||
name (str): Name of the item.
|
||||
value (str): Value of the item.
|
||||
|
||||
.. _Keyring module:
|
||||
https://github.com/jaraco/keyring
|
||||
|
||||
"""
|
||||
if six.PY2:
|
||||
raise NotImplementedError(
|
||||
"Keyring not available on Python 2 hosts")
|
||||
import keyring
|
||||
keyring.set_password(self._name, name, value)
|
||||
|
||||
@lru_cache(maxsize=32)
|
||||
def get_secure_item(self, name):
|
||||
# type: (str) -> str
|
||||
"""Get value of sensitive item from system's keyring.
|
||||
|
||||
See also `Keyring module`_
|
||||
|
||||
Args:
|
||||
name (str): Name of the item.
|
||||
|
||||
Returns:
|
||||
value (str): Value of the item.
|
||||
|
||||
Raises:
|
||||
ValueError: If item doesn't exist.
|
||||
|
||||
.. _Keyring module:
|
||||
https://github.com/jaraco/keyring
|
||||
|
||||
"""
|
||||
if six.PY2:
|
||||
raise NotImplementedError(
|
||||
"Keyring not available on Python 2 hosts")
|
||||
import keyring
|
||||
value = keyring.get_password(self._name, name)
|
||||
if not value:
|
||||
raise ValueError(
|
||||
"Item {}:{} does not exist in keyring.".format(
|
||||
self._name, name))
|
||||
return value
|
||||
|
||||
def delete_secure_item(self, name):
|
||||
# type: (str) -> None
|
||||
"""Delete value stored in system's keyring.
|
||||
|
||||
See also `Keyring module`_
|
||||
|
||||
Args:
|
||||
name (str): Name of the item to be deleted.
|
||||
|
||||
.. _Keyring module:
|
||||
https://github.com/jaraco/keyring
|
||||
|
||||
"""
|
||||
if six.PY2:
|
||||
raise NotImplementedError(
|
||||
"Keyring not available on Python 2 hosts")
|
||||
import keyring
|
||||
self.get_secure_item.cache_clear()
|
||||
keyring.delete_password(self._name, name)
|
||||
|
||||
|
||||
class IniSettingRegistry(ASettingRegistry):
|
||||
"""Class using :mod:`configparser`.
|
||||
|
||||
This class is using :mod:`configparser` (ini) files to store items.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name, path):
|
||||
# type: (str, str) -> IniSettingRegistry
|
||||
super(IniSettingRegistry, self).__init__(name)
|
||||
# get registry file
|
||||
version = os.getenv("PYPE_VERSION", "N/A")
|
||||
self._registry_file = os.path.join(path, "{}.ini".format(name))
|
||||
if not os.path.exists(self._registry_file):
|
||||
with open(self._registry_file, mode="w") as cfg:
|
||||
print("# Settings registry", cfg)
|
||||
print("# Generated by Pype {}".format(version), cfg)
|
||||
now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
|
||||
print("# {}".format(now), cfg)
|
||||
|
||||
def set_item_section(
|
||||
self, section, name, value):
|
||||
# type: (str, str, str) -> None
|
||||
"""Set item to specific section of ini registry.
|
||||
|
||||
If section doesn't exists, it is created.
|
||||
|
||||
Args:
|
||||
section (str): Name of section.
|
||||
name (str): Name of the item.
|
||||
value (str): Value of the item.
|
||||
|
||||
"""
|
||||
value = str(value)
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
config.read(self._registry_file)
|
||||
if not config.has_section(section):
|
||||
config.add_section(section)
|
||||
current = config[section]
|
||||
current[name] = value
|
||||
|
||||
with open(self._registry_file, mode="w") as cfg:
|
||||
config.write(cfg)
|
||||
|
||||
def _set_item(self, name, value):
|
||||
# type: (str, str) -> None
|
||||
self.set_item_section("MAIN", name, value)
|
||||
|
||||
def set_item(self, name, value):
|
||||
# type: (str, str) -> None
|
||||
"""Set item to settings ini file.
|
||||
|
||||
This saves item to ``DEFAULT`` section of ini as each item there
|
||||
must reside in some section.
|
||||
|
||||
Args:
|
||||
name (str): Name of the item.
|
||||
value (str): Value of the item.
|
||||
|
||||
"""
|
||||
# this does the some, overridden just for different docstring.
|
||||
# we cast value to str as ini options values must be strings.
|
||||
super(IniSettingRegistry, self).set_item(name, str(value))
|
||||
|
||||
def get_item(self, name):
|
||||
# type: (str) -> str
|
||||
"""Gets item from settings ini file.
|
||||
|
||||
This gets settings from ``DEFAULT`` section of ini file as each item
|
||||
there must reside in some section.
|
||||
|
||||
Args:
|
||||
name (str): Name of the item.
|
||||
|
||||
Returns:
|
||||
str: Value of item.
|
||||
|
||||
Raises:
|
||||
ValueError: If value doesn't exist.
|
||||
|
||||
"""
|
||||
return super(IniSettingRegistry, self).get_item(name)
|
||||
|
||||
@lru_cache(maxsize=32)
|
||||
def get_item_from_section(self, section, name):
|
||||
# type: (str, str) -> str
|
||||
"""Get item from section of ini file.
|
||||
|
||||
This will read ini file and try to get item value from specified
|
||||
section. If that section or item doesn't exist, :exc:`ValueError`
|
||||
is risen.
|
||||
|
||||
Args:
|
||||
section (str): Name of ini section.
|
||||
name (str): Name of the item.
|
||||
|
||||
Returns:
|
||||
str: Item value.
|
||||
|
||||
Raises:
|
||||
ValueError: If value doesn't exist.
|
||||
|
||||
"""
|
||||
config = configparser.ConfigParser()
|
||||
config.read(self._registry_file)
|
||||
try:
|
||||
value = config[section][name]
|
||||
except KeyError:
|
||||
raise ValueError(
|
||||
"Registry doesn't contain value {}:{}".format(section, name))
|
||||
return value
|
||||
|
||||
def _get_item(self, name):
|
||||
# type: (str) -> str
|
||||
return self.get_item_from_section("MAIN", name)
|
||||
|
||||
def delete_item_from_section(self, section, name):
|
||||
# type: (str, str) -> None
|
||||
"""Delete item from section in ini file.
|
||||
|
||||
Args:
|
||||
section (str): Section name.
|
||||
name (str): Name of the item.
|
||||
|
||||
Raises:
|
||||
ValueError: If item doesn't exist.
|
||||
|
||||
"""
|
||||
self.get_item_from_section.cache_clear()
|
||||
config = configparser.ConfigParser()
|
||||
config.read(self._registry_file)
|
||||
try:
|
||||
_ = config[section][name]
|
||||
except KeyError:
|
||||
raise ValueError(
|
||||
"Registry doesn't contain value {}:{}".format(section, name))
|
||||
config.remove_option(section, name)
|
||||
|
||||
# if section is empty, delete it
|
||||
if len(config[section].keys()) == 0:
|
||||
config.remove_section(section)
|
||||
|
||||
with open(self._registry_file, mode="w") as cfg:
|
||||
config.write(cfg)
|
||||
|
||||
def _delete_item(self, name):
|
||||
"""Delete item from default section.
|
||||
|
||||
Note:
|
||||
See :meth:`~pype.lib.IniSettingsRegistry.delete_item_from_section`
|
||||
|
||||
"""
|
||||
self.delete_item_from_section("MAIN", name)
|
||||
|
||||
|
||||
class JSONSettingRegistry(ASettingRegistry):
|
||||
"""Class using json file as storage."""
|
||||
|
||||
def __init__(self, name, path):
|
||||
# type: (str, str) -> JSONSettingRegistry
|
||||
super(JSONSettingRegistry, self).__init__(name)
|
||||
#: str: name of registry file
|
||||
self._registry_file = os.path.join(path, "{}.json".format(name))
|
||||
now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
|
||||
header = {
|
||||
"__metadata__": {
|
||||
"pype-version": os.getenv("PYPE_VERSION", "N/A"),
|
||||
"generated": now
|
||||
},
|
||||
"registry": {}
|
||||
}
|
||||
|
||||
if not os.path.exists(os.path.dirname(self._registry_file)):
|
||||
os.makedirs(os.path.dirname(self._registry_file), exist_ok=True)
|
||||
if not os.path.exists(self._registry_file):
|
||||
with open(self._registry_file, mode="w") as cfg:
|
||||
json.dump(header, cfg, indent=4)
|
||||
|
||||
@lru_cache(maxsize=32)
|
||||
def _get_item(self, name):
|
||||
# type: (str) -> object
|
||||
"""Get item value from registry json.
|
||||
|
||||
Note:
|
||||
See :meth:`pype.lib.JSONSettingRegistry.get_item`
|
||||
|
||||
"""
|
||||
with open(self._registry_file, mode="r") as cfg:
|
||||
data = json.load(cfg)
|
||||
try:
|
||||
value = data["registry"][name]
|
||||
except KeyError:
|
||||
raise ValueError(
|
||||
"Registry doesn't contain value {}".format(name))
|
||||
return value
|
||||
|
||||
def get_item(self, name):
|
||||
# type: (str) -> object
|
||||
"""Get item value from registry json.
|
||||
|
||||
Args:
|
||||
name (str): Name of the item.
|
||||
|
||||
Returns:
|
||||
value of the item
|
||||
|
||||
Raises:
|
||||
ValueError: If item is not found in registry file.
|
||||
|
||||
"""
|
||||
return self._get_item(name)
|
||||
|
||||
def _set_item(self, name, value):
|
||||
# type: (str, object) -> None
|
||||
"""Set item value to registry json.
|
||||
|
||||
Note:
|
||||
See :meth:`pype.lib.JSONSettingRegistry.set_item`
|
||||
|
||||
"""
|
||||
with open(self._registry_file, "r+") as cfg:
|
||||
data = json.load(cfg)
|
||||
data["registry"][name] = value
|
||||
cfg.truncate(0)
|
||||
cfg.seek(0)
|
||||
json.dump(data, cfg, indent=4)
|
||||
|
||||
def set_item(self, name, value):
|
||||
# type: (str, object) -> None
|
||||
"""Set item and its value into json registry file.
|
||||
|
||||
Args:
|
||||
name (str): name of the item.
|
||||
value (Any): value of the item.
|
||||
|
||||
"""
|
||||
self._set_item(name, value)
|
||||
|
||||
def _delete_item(self, name):
|
||||
# type: (str) -> None
|
||||
self._get_item.cache_clear()
|
||||
with open(self._registry_file, "r+") as cfg:
|
||||
data = json.load(cfg)
|
||||
del data["registry"][name]
|
||||
cfg.truncate(0)
|
||||
cfg.seek(0)
|
||||
json.dump(data, cfg, indent=4)
|
||||
|
||||
|
||||
class PypeSettingsRegistry(JSONSettingRegistry):
|
||||
"""Class handling Pype general settings registry.
|
||||
|
||||
Attributes:
|
||||
vendor (str): Name used for path construction.
|
||||
product (str): Additional name used for path construction.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.vendor = "pypeclub"
|
||||
self.product = "pype"
|
||||
path = appdirs.user_data_dir(self.product, self.vendor)
|
||||
super(PypeSettingsRegistry, self).__init__("pype_settings", path)
|
||||
268
pype.py
268
pype.py
|
|
@ -1,268 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Main entry point for Pype command.
|
||||
|
||||
Bootstrapping process of Pype is as follows:
|
||||
|
||||
`PYPE_PATH` is checked for existence - either one from environment or
|
||||
from user settings. Precedence takes the one set by environment.
|
||||
|
||||
On this path we try to find zip files with `pype-repositories-v3.x.x.zip`
|
||||
format.
|
||||
|
||||
If no Pype repositories are found in `PYPE_PATH (user data dir)
|
||||
then **Igniter** (Pype setup tool) will launch its GUI.
|
||||
|
||||
It can be used to specify `PYPE_PATH` or if it is _not_ specified, current
|
||||
*"live"* repositories will be used to create such zip file and copy it to
|
||||
appdata dir in user home. Version will be determined by version specified
|
||||
in Pype module.
|
||||
|
||||
If Pype repositories zip file is found in default install location
|
||||
(user data dir) or in `PYPE_PATH`, it will get list of those zips there and
|
||||
use latest one or the one specified with optional `--use-version` command
|
||||
line argument. If the one specified doesn't exist then latest available
|
||||
version will be used. All repositories in that zip will be added
|
||||
to `sys.path` and `PYTHONPATH`.
|
||||
|
||||
If Pype is live (not frozen) then current version of Pype module will be
|
||||
used. All directories under `repos` will be added to `sys.path` and
|
||||
`PYTHONPATH`.
|
||||
|
||||
Pype depends on connection to `MongoDB`_. You can specify MongoDB connection
|
||||
string via `AVALON_MONGO` set in environment or it can be set in user
|
||||
settings or via **Igniter** GUI.
|
||||
|
||||
Todo:
|
||||
Move or remove bootstrapping environments out of the code.
|
||||
|
||||
.. _MongoDB:
|
||||
https://www.mongodb.com/
|
||||
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from igniter.tools import load_environments, add_acre_to_sys_path
|
||||
|
||||
from igniter import BootstrapRepos
|
||||
|
||||
try:
|
||||
import acre
|
||||
except ImportError:
|
||||
add_acre_to_sys_path()
|
||||
import acre
|
||||
|
||||
|
||||
def set_environments() -> None:
|
||||
"""Set loaded environments.
|
||||
|
||||
.. todo:
|
||||
better handling of environments
|
||||
|
||||
"""
|
||||
# FIXME: remove everything except global
|
||||
env = load_environments(["global"])
|
||||
env = acre.merge(env, dict(os.environ))
|
||||
os.environ.clear()
|
||||
os.environ.update(env)
|
||||
|
||||
|
||||
def set_modules_environments():
|
||||
"""Set global environments for pype's modules.
|
||||
|
||||
This requires to have pype in `sys.path`.
|
||||
"""
|
||||
|
||||
from pype.modules import ModulesManager
|
||||
|
||||
modules_manager = ModulesManager()
|
||||
|
||||
module_envs = modules_manager.collect_global_environments()
|
||||
publish_plugin_dirs = modules_manager.collect_plugin_paths()["publish"]
|
||||
|
||||
# Set pyblish plugins paths if any module want to register them
|
||||
if publish_plugin_dirs:
|
||||
publish_paths_str = os.environ.get("PYBLISHPLUGINPATH") or ""
|
||||
publish_paths = publish_paths_str.split(os.pathsep)
|
||||
_publish_paths = set()
|
||||
for path in publish_paths:
|
||||
if path:
|
||||
_publish_paths.add(os.path.normpath(path))
|
||||
for path in publish_plugin_dirs:
|
||||
_publish_paths.add(os.path.normpath(path))
|
||||
module_envs["PYBLISHPLUGINPATH"] = os.pathsep.join(_publish_paths)
|
||||
|
||||
# Metge environments with current environments and update values
|
||||
if module_envs:
|
||||
parsed_envs = acre.parse(module_envs)
|
||||
env = acre.merge(parsed_envs, dict(os.environ))
|
||||
os.environ.clear()
|
||||
os.environ.update(env)
|
||||
|
||||
|
||||
def boot():
|
||||
"""Bootstrap Pype."""
|
||||
|
||||
from pype.lib.terminal_splash import play_animation
|
||||
play_animation()
|
||||
|
||||
# find pype versions
|
||||
bootstrap = BootstrapRepos()
|
||||
pype_versions = bootstrap.find_pype()
|
||||
|
||||
# check for `--use-version=3.0.0` argument.
|
||||
use_version = None
|
||||
|
||||
for arg in sys.argv:
|
||||
m = re.search(r"--use-version=(?P<version>\d+\.\d+\.\d*.+?)", arg)
|
||||
if m and m.group('version'):
|
||||
use_version = m.group('version')
|
||||
break
|
||||
|
||||
if not os.getenv("PYPE_MONGO"):
|
||||
try:
|
||||
pype_mongo = bootstrap.registry.get_secure_item("pypeMongo")
|
||||
except ValueError:
|
||||
print("*** No DB connection string specified.")
|
||||
print("--- launching setup UI ...")
|
||||
import igniter
|
||||
igniter.run()
|
||||
return
|
||||
else:
|
||||
os.environ["PYPE_MONGO"] = pype_mongo
|
||||
|
||||
set_environments()
|
||||
if getattr(sys, 'frozen', False):
|
||||
if not pype_versions:
|
||||
import igniter
|
||||
igniter.run()
|
||||
|
||||
version_path = BootstrapRepos.get_version_path_from_list(
|
||||
use_version, pype_versions)
|
||||
if version_path:
|
||||
# use specified
|
||||
bootstrap.add_paths_from_archive(version_path)
|
||||
|
||||
else:
|
||||
if use_version is not None:
|
||||
print(("!!! Specified version was not found, using "
|
||||
"latest available"))
|
||||
# use latest
|
||||
version_path = pype_versions[-1].path
|
||||
bootstrap.add_paths_from_archive(version_path)
|
||||
use_version = str(pype_versions[-1])
|
||||
|
||||
os.environ["PYPE_ROOT"] = version_path.as_posix()
|
||||
else:
|
||||
# run through repos and add them to sys.path and PYTHONPATH
|
||||
pype_root = os.path.dirname(os.path.realpath(__file__))
|
||||
local_version = bootstrap.get_local_version()
|
||||
if use_version and use_version != local_version:
|
||||
version_path = BootstrapRepos.get_version_path_from_list(
|
||||
use_version, pype_versions)
|
||||
if version_path:
|
||||
# use specified
|
||||
bootstrap.add_paths_from_archive(version_path)
|
||||
|
||||
os.environ["PYPE_ROOT"] = pype_root
|
||||
repos = os.listdir(os.path.join(pype_root, "repos"))
|
||||
repos = [os.path.join(pype_root, "repos", repo) for repo in repos]
|
||||
# add self to python paths
|
||||
repos.insert(0, pype_root)
|
||||
for repo in repos:
|
||||
sys.path.append(repo)
|
||||
|
||||
pythonpath = os.getenv("PYTHONPATH", "")
|
||||
paths = pythonpath.split(os.pathsep)
|
||||
paths += repos
|
||||
os.environ["PYTHONPATH"] = os.pathsep.join(paths)
|
||||
|
||||
# DEPRECATED: remove when `pype-config` dissolves into Pype for good.
|
||||
# .-=-----------------------=-=. ^ .=-=--------------------------=-.
|
||||
os.environ["PYPE_MODULE_ROOT"] = os.environ["PYPE_ROOT"]
|
||||
|
||||
# delete Pype module from cache so it is used from specific version
|
||||
try:
|
||||
del sys.modules["pype"]
|
||||
del sys.modules["pype.version"]
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
from pype import cli
|
||||
from pype.lib import terminal as t
|
||||
from pype.version import __version__
|
||||
print(">>> loading environments ...")
|
||||
set_environments()
|
||||
set_modules_environments()
|
||||
|
||||
info = get_info()
|
||||
info.insert(0, ">>> Using Pype from [ {} ]".format(
|
||||
os.path.dirname(cli.__file__)))
|
||||
|
||||
info_length = len(max(info, key=len))
|
||||
info.insert(0, f"*** Pype [{__version__}] " + "-" * info_length)
|
||||
for i in info:
|
||||
t.echo(i)
|
||||
|
||||
try:
|
||||
cli.main(obj={}, prog_name="pype")
|
||||
except Exception:
|
||||
exc_info = sys.exc_info()
|
||||
print("!!! Pype crashed:")
|
||||
traceback.print_exception(*exc_info)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_info() -> list:
|
||||
"""Print additional information to console."""
|
||||
from pype.lib.mongo import get_default_components
|
||||
from pype.lib.log import PypeLogger
|
||||
|
||||
components = get_default_components()
|
||||
|
||||
infos = []
|
||||
if not getattr(sys, 'frozen', False):
|
||||
infos.append(("Pype variant", "staging"))
|
||||
else:
|
||||
infos.append(("Pype variant", "production"))
|
||||
infos.append(("Running pype from", os.environ.get('PYPE_ROOT')))
|
||||
infos.append(("Using mongodb", components["host"]))
|
||||
|
||||
if os.environ.get("FTRACK_SERVER"):
|
||||
infos.append(("Using FTrack at",
|
||||
os.environ.get("FTRACK_SERVER")))
|
||||
|
||||
if os.environ.get('DEADLINE_REST_URL'):
|
||||
infos.append(("Using Deadline webservice at",
|
||||
os.environ.get("DEADLINE_REST_URL")))
|
||||
|
||||
if os.environ.get('MUSTER_REST_URL'):
|
||||
infos.append(("Using Muster at",
|
||||
os.environ.get("MUSTER_REST_URL")))
|
||||
|
||||
# Reinitialize
|
||||
PypeLogger.initialize()
|
||||
|
||||
log_components = PypeLogger.log_mongo_url_components
|
||||
if log_components["host"]:
|
||||
infos.append(("Logging to MongoDB", log_components["host"]))
|
||||
infos.append((" - port", log_components["port"] or "<N/A>"))
|
||||
infos.append((" - database", PypeLogger.log_database_name))
|
||||
infos.append((" - collection", PypeLogger.log_collection_name))
|
||||
infos.append((" - user", log_components["username"] or "<N/A>"))
|
||||
if log_components["auth_db"]:
|
||||
infos.append((" - auth source", log_components["auth_db"]))
|
||||
|
||||
maximum = max([len(i[0]) for i in infos])
|
||||
formatted = []
|
||||
for info in infos:
|
||||
padding = (maximum - len(info[0])) + 1
|
||||
formatted.append(
|
||||
"... {}:{}[ {} ]".format(info[0], " " * padding, info[1]))
|
||||
return formatted
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
boot()
|
||||
10
pype/api.py
10
pype/api.py
|
|
@ -26,6 +26,14 @@ from .lib.mongo import (
|
|||
get_default_components
|
||||
)
|
||||
|
||||
from .lib.applications import (
|
||||
ApplicationManager
|
||||
)
|
||||
|
||||
from .lib.avalon_context import (
|
||||
BuildWorkfile
|
||||
)
|
||||
|
||||
from . import resources
|
||||
|
||||
from .plugin import (
|
||||
|
|
@ -63,6 +71,8 @@ __all__ = [
|
|||
"decompose_url",
|
||||
"compose_url",
|
||||
"get_default_components",
|
||||
"ApplicationManager",
|
||||
"BuildWorkfile",
|
||||
|
||||
# Resources
|
||||
"resources",
|
||||
|
|
|
|||
88
pype/cli.py
88
pype/cli.py
|
|
@ -1,13 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package for handling pype command line arguments."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
import click
|
||||
|
||||
# import sys
|
||||
from .pype_commands import PypeCommands
|
||||
import click
|
||||
|
||||
|
||||
@click.group(invoke_without_command=True)
|
||||
@click.pass_context
|
||||
@click.option("--use-version",
|
||||
expose_value=False, help="use specified version")
|
||||
@click.option("--use-staging", is_flag=True,
|
||||
expose_value=False, help="use staging variants")
|
||||
def main(ctx):
|
||||
"""Pype is main command serving as entry point to pipeline system.
|
||||
|
||||
|
|
@ -20,6 +27,7 @@ def main(ctx):
|
|||
@main.command()
|
||||
@click.option("-d", "--dev", is_flag=True, help="Settings in Dev mode")
|
||||
def settings(dev=False):
|
||||
"""Show Pype Settings UI."""
|
||||
PypeCommands().launch_settings_gui(dev)
|
||||
|
||||
|
||||
|
|
@ -38,12 +46,6 @@ def tray(debug=False):
|
|||
PypeCommands().launch_tray(debug)
|
||||
|
||||
|
||||
@main.command()
|
||||
def mongodb():
|
||||
"""Launch local mongodb server. Useful for development."""
|
||||
PypeCommands().launch_local_mongodb()
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option("-d", "--debug", is_flag=True, help="Print debug messages")
|
||||
@click.option("--ftrack-url", envvar="FTRACK_SERVER",
|
||||
|
|
@ -56,7 +58,7 @@ def mongodb():
|
|||
envvar="FTRACK_EVENTS_PATH",
|
||||
help=("path to ftrack event handlers"))
|
||||
@click.option("--no-stored-credentials", is_flag=True,
|
||||
help="dont use stored credentials")
|
||||
help="don't use stored credentials")
|
||||
@click.option("--store-credentials", is_flag=True,
|
||||
help="store provided credentials")
|
||||
@click.option("--legacy", is_flag=True,
|
||||
|
|
@ -165,41 +167,6 @@ def texturecopy(debug, project, asset, path):
|
|||
PypeCommands().texture_copy(project, asset, path)
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.option("-k", "--keyword", help="select tests by keyword to run",
|
||||
type=click.STRING)
|
||||
@click.argument("id", nargs=-1, type=click.STRING)
|
||||
def test(pype, keyword, id):
|
||||
"""Run test suite."""
|
||||
if pype:
|
||||
PypeCommands().run_pype_tests(keyword, id)
|
||||
|
||||
|
||||
@main.command()
|
||||
def make_docs():
|
||||
"""Generate documentation with Sphinx into `docs/build`."""
|
||||
PypeCommands().make_docs()
|
||||
|
||||
|
||||
@main.command()
|
||||
def coverage():
|
||||
"""Generate code coverage report."""
|
||||
PypeCommands().pype_setup_coverage()
|
||||
|
||||
|
||||
@main.command()
|
||||
def clean():
|
||||
"""Delete python bytecode files.
|
||||
|
||||
Working throughout Pype directory, it will remove all pyc bytecode files.
|
||||
This is normally not needed but there are cases when update of repostories
|
||||
caused errors thanks to these files. If you encounter errors complaining
|
||||
about `magic number`, run this command.
|
||||
"""
|
||||
# TODO: reimplement in Python
|
||||
pass
|
||||
|
||||
|
||||
@main.command(context_settings={"ignore_unknown_options": True})
|
||||
@click.option("--app", help="Registered application name")
|
||||
@click.option("--project", help="Project name",
|
||||
|
|
@ -233,7 +200,9 @@ def launch(app, project, asset, task,
|
|||
Optionally you can specify ftrack credentials if needed.
|
||||
|
||||
ARGUMENTS are passed to launched application.
|
||||
|
||||
"""
|
||||
# TODO: this needs to switch for Settings
|
||||
if ftrack_server:
|
||||
os.environ["FTRACK_SERVER"] = ftrack_server
|
||||
|
||||
|
|
@ -255,6 +224,33 @@ def launch(app, project, asset, task,
|
|||
|
||||
|
||||
@main.command()
|
||||
def validate_config():
|
||||
"""Validate all json configuration files for errors."""
|
||||
PypeCommands().validate_jsons()
|
||||
@click.option("-p", "--path", help="Path to zip file", default=None)
|
||||
def generate_zip(path):
|
||||
"""Generate Pype zip from current sources.
|
||||
|
||||
If PATH is not provided, it will create zip file in user data dir.
|
||||
|
||||
"""
|
||||
PypeCommands().generate_zip(path)
|
||||
|
||||
|
||||
@main.command(
|
||||
context_settings=dict(
|
||||
ignore_unknown_options=True,
|
||||
allow_extra_args=True))
|
||||
@click.argument("script", required=True, type=click.Path(exists=True))
|
||||
def run(script):
|
||||
"""Run python script in Pype context."""
|
||||
import runpy
|
||||
|
||||
if not script:
|
||||
print("Error: missing path to script file.")
|
||||
else:
|
||||
|
||||
args = sys.argv
|
||||
args.remove("run")
|
||||
args.remove(script)
|
||||
sys.argv = args
|
||||
args_string = " ".join(args[1:])
|
||||
print(f"... running: {script} {args_string}")
|
||||
runpy.run_path(script, run_name="__main__", )
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class HarmonyPrelaunchHook(PreLaunchHook):
|
|||
(
|
||||
"import avalon.harmony;"
|
||||
"avalon.harmony.launch(\"{}\")"
|
||||
).format(harmony_executable)
|
||||
).format(harmony_executable.replace("\\", "/"))
|
||||
]
|
||||
|
||||
# Append as whole list as these areguments should not be separated
|
||||
|
|
|
|||
131
pype/hooks/photoshop/prelaunch.py
Normal file
131
pype/hooks/photoshop/prelaunch.py
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
import os
|
||||
import pype.lib
|
||||
from pype.api import Logger, Anatomy
|
||||
import shutil
|
||||
import getpass
|
||||
import avalon.api
|
||||
|
||||
|
||||
class PhotoshopPrelaunch(pype.lib.PypeHook):
|
||||
"""This hook will check for the existence of PyWin
|
||||
|
||||
PyWin is a requirement for the Photoshop integration.
|
||||
"""
|
||||
project_code = None
|
||||
host_name = "photoshop"
|
||||
|
||||
def __init__(self, logger=None):
|
||||
if not logger:
|
||||
self.log = Logger().get_logger(self.__class__.__name__)
|
||||
else:
|
||||
self.log = logger
|
||||
|
||||
self.signature = "( {} )".format(self.__class__.__name__)
|
||||
|
||||
def execute(self, *args, env: dict = None) -> bool:
|
||||
output = pype.lib._subprocess(["pip", "install", "pywin32==227"])
|
||||
self.log.info(output)
|
||||
|
||||
workfile_path = self.get_workfile_plath(env, self.host_name)
|
||||
|
||||
# adding compulsory environment var for openting file
|
||||
env["PYPE_WORKFILE_PATH"] = workfile_path
|
||||
|
||||
return True
|
||||
|
||||
def get_anatomy_filled(self, workdir, project_name, asset_name,
|
||||
task_name, host_name, extension):
|
||||
dbcon = avalon.api.AvalonMongoDB()
|
||||
dbcon.install()
|
||||
dbcon.Session["AVALON_PROJECT"] = project_name
|
||||
project_document = dbcon.find_one({"type": "project"})
|
||||
asset_document = dbcon.find_one({
|
||||
"type": "asset",
|
||||
"name": asset_name
|
||||
})
|
||||
dbcon.uninstall()
|
||||
|
||||
asset_doc_parents = asset_document["data"].get("parents")
|
||||
hierarchy = "/".join(asset_doc_parents)
|
||||
|
||||
data = {
|
||||
"project": {
|
||||
"name": project_document["name"],
|
||||
"code": project_document["data"].get("code")
|
||||
},
|
||||
"task": task_name,
|
||||
"asset": asset_name,
|
||||
"app": host_name,
|
||||
"hierarchy": hierarchy
|
||||
}
|
||||
anatomy = Anatomy(project_name)
|
||||
file_template = anatomy.templates["work"]["file"]
|
||||
data.update({
|
||||
"version": 1,
|
||||
"user": os.environ.get("PYPE_USERNAME") or getpass.getuser(),
|
||||
"ext": extension
|
||||
})
|
||||
|
||||
return avalon.api.last_workfile(
|
||||
workdir, file_template, data,
|
||||
avalon.api.HOST_WORKFILE_EXTENSIONS[host_name], True
|
||||
)
|
||||
|
||||
def get_workfile_plath(self, env, host_name):
|
||||
# get context variables
|
||||
project_name = env["AVALON_PROJECT"]
|
||||
asset_name = env["AVALON_ASSET"]
|
||||
task_name = env["AVALON_TASK"]
|
||||
workdir = env["AVALON_WORKDIR"]
|
||||
extension = avalon.api.HOST_WORKFILE_EXTENSIONS[host_name][0]
|
||||
template_env_key = "{}_TEMPLATE".format(host_name.upper())
|
||||
|
||||
# get workfile path
|
||||
workfile_path = self.get_anatomy_filled(
|
||||
workdir, project_name, asset_name, task_name, host_name, extension)
|
||||
|
||||
# create workdir if doesn't exist
|
||||
os.makedirs(workdir, exist_ok=True)
|
||||
self.log.info("Work dir is: `{}`".format(workdir))
|
||||
|
||||
# get last version of workfile
|
||||
workfile_last = env.get("AVALON_LAST_WORKFILE")
|
||||
self.log.debug("_ workfile_last: `{}`".format(workfile_last))
|
||||
|
||||
if workfile_last:
|
||||
workfile = workfile_last
|
||||
workfile_path = os.path.join(workdir, workfile)
|
||||
|
||||
# copy workfile from template if doesnt exist any on path
|
||||
if not os.path.isfile(workfile_path):
|
||||
# try to get path from environment or use default
|
||||
# from `pype.hosts.<host_name>` dir
|
||||
template_path = env.get(template_env_key) or os.path.join(
|
||||
env.get("PYPE_MODULE_ROOT"),
|
||||
"pype/hosts/{}/template{}".format(host_name, extension)
|
||||
)
|
||||
|
||||
# try to get template from project config folder
|
||||
proj_config_path = os.path.join(
|
||||
env["PYPE_PROJECT_CONFIGS"], project_name)
|
||||
if os.path.exists(proj_config_path):
|
||||
|
||||
template_file = None
|
||||
for f in os.listdir(proj_config_path):
|
||||
if extension in os.path.splitext(f):
|
||||
template_file = f
|
||||
|
||||
if template_file:
|
||||
template_path = os.path.join(
|
||||
proj_config_path, template_file)
|
||||
self.log.info(
|
||||
"Creating workfile from template: `{}`".format(template_path))
|
||||
|
||||
# copy template to new destinantion
|
||||
shutil.copy2(
|
||||
os.path.normpath(template_path),
|
||||
os.path.normpath(workfile_path)
|
||||
)
|
||||
|
||||
self.log.info("Workfile to open: `{}`".format(workfile_path))
|
||||
return workfile_path
|
||||
|
|
@ -9,7 +9,7 @@ import avalon.tools.sceneinventory
|
|||
import pyblish.api
|
||||
|
||||
from pype import lib
|
||||
from pype.api import get_current_project_settings
|
||||
from pype.api import (get_current_project_settings)
|
||||
|
||||
|
||||
def set_scene_settings(settings):
|
||||
|
|
@ -48,20 +48,13 @@ def get_asset_settings():
|
|||
"resolutionWidth": resolution_width,
|
||||
"resolutionHeight": resolution_height
|
||||
}
|
||||
settings = get_current_project_settings()
|
||||
|
||||
try:
|
||||
skip_resolution_check = (
|
||||
get_current_project_settings()
|
||||
["harmony"]
|
||||
["general"]
|
||||
["skip_resolution_check"]
|
||||
)
|
||||
skip_timelines_check = (
|
||||
get_current_project_settings()
|
||||
["harmony"]
|
||||
["general"]
|
||||
["skip_timelines_check"]
|
||||
)
|
||||
skip_resolution_check = \
|
||||
settings["harmony"]["general"]["skip_resolution_check"]
|
||||
skip_timelines_check = \
|
||||
settings["harmony"]["general"]["skip_timelines_check"]
|
||||
except KeyError:
|
||||
skip_resolution_check = []
|
||||
skip_timelines_check = []
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
var LD_OPENHARMONY_PATH = System.getenv('LIB_OPENHARMONY_PATH');
|
||||
include(LD_OPENHARMONY_PATH + '/openHarmony.js');
|
||||
this.__proto__['$'] = $;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -79,7 +79,8 @@ PypeHarmony.getSceneSettings = function() {
|
|||
scene.getStopFrame(),
|
||||
sound.getSoundtrackAll().path(),
|
||||
scene.defaultResolutionX(),
|
||||
scene.defaultResolutionY()
|
||||
scene.defaultResolutionY(),
|
||||
scene.defaultResolutionFOV()
|
||||
];
|
||||
};
|
||||
|
||||
|
|
@ -200,3 +201,16 @@ PypeHarmony.getDependencies = function(_node) {
|
|||
}
|
||||
return dependencies;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* return version of running Harmony instance.
|
||||
* @function
|
||||
* @return {array} [major_version, minor_version]
|
||||
*/
|
||||
PypeHarmony.getVersion = function() {
|
||||
return [
|
||||
about.getMajorVersion(),
|
||||
about.getMinorVersion()
|
||||
];
|
||||
};
|
||||
|
|
|
|||
52
pype/hosts/harmony/js/publish/CollectFarmRender.js
Normal file
52
pype/hosts/harmony/js/publish/CollectFarmRender.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/* global PypeHarmony:writable, include */
|
||||
// ***************************************************************************
|
||||
// * CollectFarmRender *
|
||||
// ***************************************************************************
|
||||
|
||||
|
||||
// check if PypeHarmony is defined and if not, load it.
|
||||
if (typeof PypeHarmony !== 'undefined') {
|
||||
var PYPE_HARMONY_JS = System.getenv('PYPE_HARMONY_JS');
|
||||
include(PYPE_HARMONY_JS + '/pype_harmony.js');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @namespace
|
||||
* @classdesc Image Sequence loader JS code.
|
||||
*/
|
||||
var CollectFarmRender = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Get information important for render output.
|
||||
* @function
|
||||
* @param node {String} node name.
|
||||
* @return {array} array of render info.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* var ret = [
|
||||
* file_prefix, // like foo/bar-
|
||||
* type, // PNG4, ...
|
||||
* leading_zeros, // 3 - for 0001
|
||||
* start // start frame
|
||||
* ]
|
||||
*/
|
||||
CollectFarmRender.prototype.getRenderNodeSettings = function(n) {
|
||||
// this will return
|
||||
var output = [
|
||||
node.getTextAttr(
|
||||
n, frame.current(), 'DRAWING_NAME'),
|
||||
node.getTextAttr(
|
||||
n, frame.current(), 'DRAWING_TYPE'),
|
||||
node.getTextAttr(
|
||||
n, frame.current(), 'LEADING_ZEROS'),
|
||||
node.getTextAttr(n, frame.current(), 'START')
|
||||
];
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
// add self to Pype Loaders
|
||||
PypeHarmony.Publish.CollectFarmRender = new CollectFarmRender();
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
import os
|
||||
import sys
|
||||
import logging
|
||||
|
||||
import nuke
|
||||
|
||||
from avalon import api as avalon
|
||||
from avalon.tools import workfiles
|
||||
from pyblish import api as pyblish
|
||||
from pype.hosts.nuke import menu
|
||||
from pype.api import Logger
|
||||
from pype import PLUGINS_DIR
|
||||
from . import lib
|
||||
|
||||
|
||||
self = sys.modules[__name__]
|
||||
self.workfiles_launched = False
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
|
||||
|
||||
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "nuke", "publish")
|
||||
LOAD_PATH = os.path.join(PLUGINS_DIR, "nuke", "load")
|
||||
CREATE_PATH = os.path.join(PLUGINS_DIR, "nuke", "create")
|
||||
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "nuke", "inventory")
|
||||
|
||||
|
||||
# registering pyblish gui regarding settings in presets
|
||||
if os.getenv("PYBLISH_GUI", None):
|
||||
pyblish.register_gui(os.getenv("PYBLISH_GUI", None))
|
||||
|
||||
|
||||
def reload_config():
|
||||
"""Attempt to reload pipeline at run-time.
|
||||
|
||||
CAUTION: This is primarily for development and debugging purposes.
|
||||
|
||||
"""
|
||||
|
||||
import importlib
|
||||
|
||||
for module in (
|
||||
"{}.api".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.actions".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.presets".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.menu".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.plugin".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.lib".format(AVALON_CONFIG),
|
||||
):
|
||||
log.info("Reloading module: {}...".format(module))
|
||||
|
||||
module = importlib.import_module(module)
|
||||
|
||||
try:
|
||||
importlib.reload(module)
|
||||
except AttributeError as e:
|
||||
log.warning("Cannot reload module: {}".format(e))
|
||||
reload(module)
|
||||
|
||||
|
||||
def install():
|
||||
''' Installing all requarements for Nuke host
|
||||
'''
|
||||
|
||||
log.info("Registering Nuke plug-ins..")
|
||||
pyblish.register_plugin_path(PUBLISH_PATH)
|
||||
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
|
||||
|
||||
# Register Avalon event for workfiles loading.
|
||||
avalon.on("workio.open_file", lib.check_inventory_versions)
|
||||
|
||||
pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled)
|
||||
workfile_settings = lib.WorkfileSettings()
|
||||
# Disable all families except for the ones we explicitly want to see
|
||||
family_states = [
|
||||
"write",
|
||||
"review",
|
||||
"nukenodes"
|
||||
"gizmo"
|
||||
]
|
||||
|
||||
avalon.data["familiesStateDefault"] = False
|
||||
avalon.data["familiesStateToggled"] = family_states
|
||||
|
||||
# Workfiles.
|
||||
launch_workfiles = os.environ.get("WORKFILES_STARTUP")
|
||||
|
||||
if launch_workfiles:
|
||||
nuke.addOnCreate(launch_workfiles_app, nodeClass="Root")
|
||||
|
||||
# Set context settings.
|
||||
nuke.addOnCreate(workfile_settings.set_context_settings, nodeClass="Root")
|
||||
nuke.addOnCreate(workfile_settings.set_favorites, nodeClass="Root")
|
||||
|
||||
menu.install()
|
||||
|
||||
|
||||
def launch_workfiles_app():
|
||||
'''Function letting start workfiles after start of host
|
||||
'''
|
||||
if not self.workfiles_launched:
|
||||
self.workfiles_launched = True
|
||||
workfiles.show(os.environ["AVALON_WORKDIR"])
|
||||
|
||||
|
||||
def uninstall():
|
||||
'''Uninstalling host's integration
|
||||
'''
|
||||
log.info("Deregistering Nuke plug-ins..")
|
||||
pyblish.deregister_plugin_path(PUBLISH_PATH)
|
||||
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
|
||||
pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled)
|
||||
|
||||
|
||||
reload_config()
|
||||
menu.uninstall()
|
||||
|
||||
|
||||
def on_pyblish_instance_toggled(instance, old_value, new_value):
|
||||
"""Toggle node passthrough states on instance toggles."""
|
||||
|
||||
log.info("instance toggle: {}, old_value: {}, new_value:{} ".format(
|
||||
instance, old_value, new_value))
|
||||
|
||||
from avalon.nuke import (
|
||||
viewer_update_and_undo_stop,
|
||||
add_publish_knob
|
||||
)
|
||||
|
||||
# Whether instances should be passthrough based on new value
|
||||
|
||||
with viewer_update_and_undo_stop():
|
||||
n = instance[0]
|
||||
try:
|
||||
n["publish"].value()
|
||||
except ValueError:
|
||||
n = add_publish_knob(n)
|
||||
log.info(" `Publish` knob was added to write node..")
|
||||
|
||||
n["publish"].setValue(new_value)
|
||||
141
pype/hosts/nuke/api/__init__.py
Normal file
141
pype/hosts/nuke/api/__init__.py
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
import os
|
||||
import sys
|
||||
import nuke
|
||||
|
||||
from avalon import api as avalon
|
||||
from avalon.tools import workfiles
|
||||
from pyblish import api as pyblish
|
||||
from pype.api import Logger
|
||||
import pype.hosts.nuke
|
||||
from . import lib, menu
|
||||
|
||||
|
||||
self = sys.modules[__name__]
|
||||
self.workfiles_launched = False
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
AVALON_CONFIG = os.getenv("AVALON_CONFIG", "pype")
|
||||
HOST_DIR = os.path.dirname(os.path.abspath(pype.hosts.nuke.__file__))
|
||||
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
|
||||
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
|
||||
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
|
||||
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
|
||||
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
|
||||
|
||||
|
||||
# registering pyblish gui regarding settings in presets
|
||||
if os.getenv("PYBLISH_GUI", None):
|
||||
pyblish.register_gui(os.getenv("PYBLISH_GUI", None))
|
||||
|
||||
|
||||
def reload_config():
|
||||
"""Attempt to reload pipeline at run-time.
|
||||
|
||||
CAUTION: This is primarily for development and debugging purposes.
|
||||
|
||||
"""
|
||||
|
||||
import importlib
|
||||
|
||||
for module in (
|
||||
"{}.api".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.api.actions".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.api.menu".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.api.plugin".format(AVALON_CONFIG),
|
||||
"{}.hosts.nuke.api.lib".format(AVALON_CONFIG),
|
||||
):
|
||||
log.info("Reloading module: {}...".format(module))
|
||||
|
||||
module = importlib.import_module(module)
|
||||
|
||||
try:
|
||||
importlib.reload(module)
|
||||
except AttributeError as e:
|
||||
from importlib import reload
|
||||
log.warning("Cannot reload module: {}".format(e))
|
||||
reload(module)
|
||||
|
||||
|
||||
def install():
|
||||
''' Installing all requarements for Nuke host
|
||||
'''
|
||||
|
||||
log.info("Registering Nuke plug-ins..")
|
||||
pyblish.register_plugin_path(PUBLISH_PATH)
|
||||
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.register_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
|
||||
|
||||
# Register Avalon event for workfiles loading.
|
||||
avalon.on("workio.open_file", lib.check_inventory_versions)
|
||||
|
||||
pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled)
|
||||
workfile_settings = lib.WorkfileSettings()
|
||||
# Disable all families except for the ones we explicitly want to see
|
||||
family_states = [
|
||||
"write",
|
||||
"review",
|
||||
"nukenodes"
|
||||
"gizmo"
|
||||
]
|
||||
|
||||
avalon.data["familiesStateDefault"] = False
|
||||
avalon.data["familiesStateToggled"] = family_states
|
||||
|
||||
# Workfiles.
|
||||
launch_workfiles = os.environ.get("WORKFILES_STARTUP")
|
||||
|
||||
if launch_workfiles:
|
||||
nuke.addOnCreate(launch_workfiles_app, nodeClass="Root")
|
||||
|
||||
# Set context settings.
|
||||
nuke.addOnCreate(workfile_settings.set_context_settings, nodeClass="Root")
|
||||
# nuke.addOnCreate(workfile_settings.set_favorites, nodeClass="Root")
|
||||
|
||||
menu.install()
|
||||
|
||||
|
||||
def launch_workfiles_app():
|
||||
'''Function letting start workfiles after start of host
|
||||
'''
|
||||
if not self.workfiles_launched:
|
||||
self.workfiles_launched = True
|
||||
workfiles.show(os.environ["AVALON_WORKDIR"])
|
||||
|
||||
|
||||
def uninstall():
|
||||
'''Uninstalling host's integration
|
||||
'''
|
||||
log.info("Deregistering Nuke plug-ins..")
|
||||
pyblish.deregister_plugin_path(PUBLISH_PATH)
|
||||
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
|
||||
avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH)
|
||||
|
||||
pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled)
|
||||
|
||||
reload_config()
|
||||
menu.uninstall()
|
||||
|
||||
|
||||
def on_pyblish_instance_toggled(instance, old_value, new_value):
|
||||
"""Toggle node passthrough states on instance toggles."""
|
||||
|
||||
log.info("instance toggle: {}, old_value: {}, new_value:{} ".format(
|
||||
instance, old_value, new_value))
|
||||
|
||||
from avalon.nuke import (
|
||||
viewer_update_and_undo_stop,
|
||||
add_publish_knob
|
||||
)
|
||||
|
||||
# Whether instances should be passthrough based on new value
|
||||
|
||||
with viewer_update_and_undo_stop():
|
||||
n = instance[0]
|
||||
try:
|
||||
n["publish"].value()
|
||||
except ValueError:
|
||||
n = add_publish_knob(n)
|
||||
log.info(" `Publish` knob was added to write node..")
|
||||
|
||||
n["publish"].setValue(new_value)
|
||||
|
|
@ -5,7 +5,7 @@ from avalon.nuke.lib import (
|
|||
select_nodes
|
||||
)
|
||||
|
||||
from ...action import get_errored_instances_from_context
|
||||
from pype.api import get_errored_instances_from_context
|
||||
|
||||
|
||||
class SelectInvalidAction(pyblish.api.Action):
|
||||
|
|
@ -6,26 +6,74 @@ from collections import OrderedDict
|
|||
from avalon import api, io, lib
|
||||
import avalon.nuke
|
||||
from avalon.nuke import lib as anlib
|
||||
import pype.api as pype
|
||||
from pype.api import (
|
||||
Logger,
|
||||
Anatomy,
|
||||
get_version_from_path,
|
||||
get_anatomy_settings,
|
||||
get_hierarchy,
|
||||
get_asset,
|
||||
config,
|
||||
ApplicationManager
|
||||
)
|
||||
|
||||
import nuke
|
||||
|
||||
|
||||
from .presets import (
|
||||
get_colorspace_preset,
|
||||
get_node_dataflow_preset,
|
||||
get_node_colorspace_preset,
|
||||
get_anatomy
|
||||
)
|
||||
|
||||
from .utils import set_context_favorites
|
||||
|
||||
log = pype.Logger().get_logger(__name__)
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
self = sys.modules[__name__]
|
||||
self._project = None
|
||||
|
||||
|
||||
def get_node_imageio_setting(**kwarg):
|
||||
''' Get preset data for dataflow (fileType, compression, bitDepth)
|
||||
'''
|
||||
log.info(kwarg)
|
||||
host = str(kwarg.get("host", "nuke"))
|
||||
nodeclass = kwarg.get("nodeclass", None)
|
||||
creator = kwarg.get("creator", None)
|
||||
project_name = os.getenv("AVALON_PROJECT")
|
||||
|
||||
assert any([host, nodeclass]), nuke.message(
|
||||
"`{}`: Missing mandatory kwargs `host`, `cls`".format(__file__))
|
||||
|
||||
imageio_nodes = (get_anatomy_settings(project_name)
|
||||
["imageio"]
|
||||
.get(host, None)
|
||||
["nodes"]
|
||||
["requiredNodes"]
|
||||
)
|
||||
|
||||
for node in imageio_nodes:
|
||||
log.info(node)
|
||||
if node["nukeNodeClass"] == nodeclass:
|
||||
if creator in node["plugins"]:
|
||||
imageio_node = node
|
||||
|
||||
log.info("ImageIO node: {}".format(imageio_node))
|
||||
return imageio_node
|
||||
|
||||
|
||||
def get_imageio_input_colorspace(filename):
|
||||
''' Get input file colorspace based on regex in settings.
|
||||
'''
|
||||
imageio_regex_inputs = (get_anatomy_settings(os.getenv("AVALON_PROJECT"))
|
||||
["imageio"]
|
||||
["nuke"]
|
||||
["regexInputs"]
|
||||
["inputs"]
|
||||
)
|
||||
|
||||
preset_clrsp = None
|
||||
for regexInput in imageio_regex_inputs:
|
||||
if bool(re.search(regexInput["regex"], filename)):
|
||||
preset_clrsp = str(regexInput["colorspace"])
|
||||
|
||||
return preset_clrsp
|
||||
|
||||
|
||||
def on_script_load():
|
||||
''' Callback for ffmpeg support
|
||||
'''
|
||||
|
|
@ -39,7 +87,7 @@ def on_script_load():
|
|||
|
||||
def check_inventory_versions():
|
||||
"""
|
||||
Actiual version idetifier of Loaded containers
|
||||
Actual version idetifier of Loaded containers
|
||||
|
||||
Any time this function is run it will check all nodes and filter only
|
||||
Loader nodes for its version. It will get all versions from database
|
||||
|
|
@ -52,9 +100,9 @@ def check_inventory_versions():
|
|||
container = avalon.nuke.parse_container(each)
|
||||
|
||||
if container:
|
||||
node = container["_node"]
|
||||
avalon_knob_data = avalon.nuke.get_avalon_knob_data(
|
||||
node, ['avalon:', 'ak:'])
|
||||
node = nuke.toNode(container["objectName"])
|
||||
avalon_knob_data = avalon.nuke.read(
|
||||
node)
|
||||
|
||||
# get representation from io
|
||||
representation = io.find_one({
|
||||
|
|
@ -88,7 +136,7 @@ def writes_version_sync():
|
|||
''' Callback synchronizing version of publishable write nodes
|
||||
'''
|
||||
try:
|
||||
rootVersion = pype.get_version_from_path(nuke.root().name())
|
||||
rootVersion = get_version_from_path(nuke.root().name())
|
||||
padding = len(rootVersion)
|
||||
new_version = "v" + str("{" + ":0>{}".format(padding) + "}").format(
|
||||
int(rootVersion)
|
||||
|
|
@ -103,8 +151,8 @@ def writes_version_sync():
|
|||
if "AvalonTab" not in each.knobs():
|
||||
continue
|
||||
|
||||
avalon_knob_data = avalon.nuke.get_avalon_knob_data(
|
||||
each, ['avalon:', 'ak:'])
|
||||
avalon_knob_data = avalon.nuke.read(
|
||||
each)
|
||||
|
||||
try:
|
||||
if avalon_knob_data['families'] not in ["render"]:
|
||||
|
|
@ -113,7 +161,7 @@ def writes_version_sync():
|
|||
|
||||
node_file = each['file'].value()
|
||||
|
||||
node_version = "v" + pype.get_version_from_path(node_file)
|
||||
node_version = "v" + get_version_from_path(node_file)
|
||||
log.debug("node_version: {}".format(node_version))
|
||||
|
||||
node_new_file = node_file.replace(node_version, new_version)
|
||||
|
|
@ -134,24 +182,40 @@ def version_up_script():
|
|||
nukescripts.script_and_write_nodes_version_up()
|
||||
|
||||
|
||||
def check_subsetname_exists(nodes, subset_name):
|
||||
"""
|
||||
Checking if node is not already created to secure there is no duplicity
|
||||
|
||||
Arguments:
|
||||
nodes (list): list of nuke.Node objects
|
||||
subset_name (str): name we try to find
|
||||
|
||||
Returns:
|
||||
bool: True of False
|
||||
"""
|
||||
result = next((True for n in nodes
|
||||
if subset_name in avalon.nuke.read(n).get("subset", "")), False)
|
||||
return result
|
||||
|
||||
|
||||
def get_render_path(node):
|
||||
''' Generate Render path from presets regarding avalon knob data
|
||||
'''
|
||||
data = dict()
|
||||
data['avalon'] = avalon.nuke.get_avalon_knob_data(
|
||||
node, ['avalon:', 'ak:'])
|
||||
data['avalon'] = avalon.nuke.read(
|
||||
node)
|
||||
|
||||
data_preset = {
|
||||
"class": data['avalon']['family'],
|
||||
"preset": data['avalon']['families']
|
||||
}
|
||||
|
||||
nuke_dataflow_writes = get_node_dataflow_preset(**data_preset)
|
||||
nuke_colorspace_writes = get_node_colorspace_preset(**data_preset)
|
||||
nuke_imageio_writes = get_node_imageio_setting(**data_preset)
|
||||
|
||||
application = lib.get_application(os.environ["AVALON_APP_NAME"])
|
||||
data.update({
|
||||
"nuke_dataflow_writes": nuke_dataflow_writes,
|
||||
"nuke_colorspace_writes": nuke_colorspace_writes
|
||||
"application": application,
|
||||
"nuke_imageio_writes": nuke_imageio_writes
|
||||
})
|
||||
|
||||
anatomy_filled = format_anatomy(data)
|
||||
|
|
@ -169,7 +233,7 @@ def format_anatomy(data):
|
|||
'''
|
||||
# TODO: perhaps should be nonPublic
|
||||
|
||||
anatomy = get_anatomy()
|
||||
anatomy = Anatomy()
|
||||
log.debug("__ anatomy.templates: {}".format(anatomy.templates))
|
||||
|
||||
try:
|
||||
|
|
@ -192,18 +256,16 @@ def format_anatomy(data):
|
|||
version = data.get("version", None)
|
||||
if not version:
|
||||
file = script_name()
|
||||
data["version"] = pype.get_version_from_path(file)
|
||||
data["version"] = get_version_from_path(file)
|
||||
project_document = io.find_one({"type": "project"})
|
||||
data.update({
|
||||
"subset": data["avalon"]["subset"],
|
||||
"asset": data["avalon"]["asset"],
|
||||
"task": api.Session["AVALON_TASK"],
|
||||
"task": os.environ["AVALON_TASK"],
|
||||
"family": data["avalon"]["family"],
|
||||
"project": {"name": project_document["name"],
|
||||
"code": project_document["data"].get("code", '')},
|
||||
"representation": data["nuke_dataflow_writes"]["file_type"],
|
||||
"app": api.Session["AVALON_APP"],
|
||||
"hierarchy": pype.get_hierarchy(),
|
||||
"hierarchy": get_hierarchy(),
|
||||
"frame": "#" * padding,
|
||||
})
|
||||
return anatomy.format(data)
|
||||
|
|
@ -217,11 +279,11 @@ def script_name():
|
|||
|
||||
def add_button_write_to_read(node):
|
||||
name = "createReadNode"
|
||||
label = "[ Create Read ]"
|
||||
label = "Cread Read From Rendered"
|
||||
value = "import write_to_read;write_to_read.write_to_read(nuke.thisNode())"
|
||||
k = nuke.PyScript_Knob(name, label, value)
|
||||
k.setFlag(0x1000)
|
||||
node.addKnob(k)
|
||||
knob = nuke.PyScript_Knob(name, label, value)
|
||||
knob.clearFlag(nuke.STARTLINE)
|
||||
node.addKnob(knob)
|
||||
|
||||
|
||||
def create_write_node(name, data, input=None, prenodes=None, review=True):
|
||||
|
|
@ -254,18 +316,26 @@ def create_write_node(name, data, input=None, prenodes=None, review=True):
|
|||
node (obj): group node with avalon data as Knobs
|
||||
'''
|
||||
|
||||
nuke_dataflow_writes = get_node_dataflow_preset(**data)
|
||||
nuke_colorspace_writes = get_node_colorspace_preset(**data)
|
||||
imageio_writes = get_node_imageio_setting(**data)
|
||||
app_manager = ApplicationManager()
|
||||
app_name = os.environ.get("AVALON_APP_NAME")
|
||||
if app_name:
|
||||
app = app_manager.applications.get(app_name)
|
||||
|
||||
for knob in imageio_writes["knobs"]:
|
||||
if knob["name"] == "file_type":
|
||||
representation = knob["value"]
|
||||
|
||||
try:
|
||||
data.update({
|
||||
"nuke_dataflow_writes": nuke_dataflow_writes,
|
||||
"nuke_colorspace_writes": nuke_colorspace_writes
|
||||
"app": app.host_name,
|
||||
"imageio_writes": imageio_writes,
|
||||
"representation": representation,
|
||||
})
|
||||
anatomy_filled = format_anatomy(data)
|
||||
|
||||
except Exception as e:
|
||||
msg = "problem with resolving anatomy tepmlate: {}".format(e)
|
||||
msg = "problem with resolving anatomy template: {}".format(e)
|
||||
log.error(msg)
|
||||
nuke.message(msg)
|
||||
|
||||
|
|
@ -274,7 +344,7 @@ def create_write_node(name, data, input=None, prenodes=None, review=True):
|
|||
fpath = data["fpath_template"].format(
|
||||
work=fpath, version=data["version"], subset=data["subset"],
|
||||
frame=data["frame"],
|
||||
ext=data["nuke_dataflow_writes"]["file_type"]
|
||||
ext=representation
|
||||
)
|
||||
|
||||
# create directory
|
||||
|
|
@ -287,17 +357,12 @@ def create_write_node(name, data, input=None, prenodes=None, review=True):
|
|||
})
|
||||
|
||||
# adding dataflow template
|
||||
log.debug("nuke_dataflow_writes: `{}`".format(nuke_dataflow_writes))
|
||||
{_data.update({k: v})
|
||||
for k, v in nuke_dataflow_writes.items()
|
||||
if k not in ["_id", "_previous"]}
|
||||
log.debug("imageio_writes: `{}`".format(imageio_writes))
|
||||
for knob in imageio_writes["knobs"]:
|
||||
if knob["name"] not in ["_id", "_previous"]:
|
||||
_data.update({knob["name"]: knob["value"]})
|
||||
|
||||
# adding colorspace template
|
||||
log.debug("nuke_colorspace_writes: `{}`".format(nuke_colorspace_writes))
|
||||
{_data.update({k: v})
|
||||
for k, v in nuke_colorspace_writes.items()}
|
||||
|
||||
_data = avalon.nuke.lib.fix_data_for_node_create(_data)
|
||||
_data = anlib.fix_data_for_node_create(_data)
|
||||
|
||||
log.debug("_data: `{}`".format(_data))
|
||||
|
||||
|
|
@ -366,7 +431,7 @@ def create_write_node(name, data, input=None, prenodes=None, review=True):
|
|||
prev_node = now_node
|
||||
|
||||
# creating write node
|
||||
write_node = now_node = avalon.nuke.lib.add_write_node(
|
||||
write_node = now_node = anlib.add_write_node(
|
||||
"inside_{}".format(name),
|
||||
**_data
|
||||
)
|
||||
|
|
@ -383,30 +448,40 @@ def create_write_node(name, data, input=None, prenodes=None, review=True):
|
|||
now_node.setInput(0, prev_node)
|
||||
|
||||
# imprinting group node
|
||||
avalon.nuke.imprint(GN, data["avalon"])
|
||||
|
||||
# add divider
|
||||
GN.addKnob(nuke.Text_Knob(''))
|
||||
|
||||
anlib.set_avalon_knob_data(GN, data["avalon"])
|
||||
anlib.add_publish_knob(GN)
|
||||
add_rendering_knobs(GN)
|
||||
|
||||
if review:
|
||||
add_review_knob(GN)
|
||||
|
||||
# add divider
|
||||
GN.addKnob(nuke.Text_Knob(''))
|
||||
GN.addKnob(nuke.Text_Knob('', 'Rendering'))
|
||||
|
||||
# Add linked knobs.
|
||||
linked_knob_names = ["Render", "use_limit", "first", "last"]
|
||||
linked_knob_names = [
|
||||
"_grp-start_",
|
||||
"use_limit", "first", "last",
|
||||
"_grp-end_",
|
||||
"Render"
|
||||
]
|
||||
for name in linked_knob_names:
|
||||
link = nuke.Link_Knob(name)
|
||||
link.makeLink(write_node.name(), name)
|
||||
link.setName(name)
|
||||
link.setFlag(0x1000)
|
||||
GN.addKnob(link)
|
||||
|
||||
# add divider
|
||||
GN.addKnob(nuke.Text_Knob(''))
|
||||
if "_grp-start_" in name:
|
||||
knob = nuke.Tab_Knob(
|
||||
"rnd_attr", "Rendering attributes", nuke.TABBEGINCLOSEDGROUP)
|
||||
GN.addKnob(knob)
|
||||
elif "_grp-end_" in name:
|
||||
knob = nuke.Tab_Knob(
|
||||
"rnd_attr_end", "Rendering attributes", nuke.TABENDGROUP)
|
||||
GN.addKnob(knob)
|
||||
else:
|
||||
link = nuke.Link_Knob("")
|
||||
link.makeLink(write_node.name(), name)
|
||||
link.setName(name)
|
||||
if "Render" in name:
|
||||
link.setLabel("Render Local")
|
||||
link.setFlag(0x1000)
|
||||
GN.addKnob(link)
|
||||
|
||||
# adding write to read button
|
||||
add_button_write_to_read(GN)
|
||||
|
|
@ -431,9 +506,9 @@ def add_rendering_knobs(node):
|
|||
node (obj): with added knobs
|
||||
'''
|
||||
if "render" not in node.knobs():
|
||||
knob = nuke.Enumeration_Knob("render", "Render", [
|
||||
knob = nuke.Enumeration_Knob("render", "", [
|
||||
"Use existing frames", "Local", "On farm"])
|
||||
knob.setFlag(0x1000)
|
||||
knob.clearFlag(nuke.STARTLINE)
|
||||
node.addKnob(knob)
|
||||
return node
|
||||
|
||||
|
|
@ -538,7 +613,7 @@ class WorkfileSettings(object):
|
|||
self._project = kwargs.get(
|
||||
"project") or io.find_one({"type": "project"})
|
||||
self._asset = kwargs.get("asset_name") or api.Session["AVALON_ASSET"]
|
||||
self._asset_entity = pype.get_asset(self._asset)
|
||||
self._asset_entity = get_asset(self._asset)
|
||||
self._root_node = root_node or nuke.root()
|
||||
self._nodes = self.get_nodes(nodes=nodes)
|
||||
|
||||
|
|
@ -674,7 +749,7 @@ class WorkfileSettings(object):
|
|||
log.error(msg)
|
||||
return
|
||||
|
||||
from avalon.nuke import get_avalon_knob_data
|
||||
from avalon.nuke import read
|
||||
|
||||
for node in nuke.allNodes():
|
||||
|
||||
|
|
@ -682,7 +757,7 @@ class WorkfileSettings(object):
|
|||
continue
|
||||
|
||||
# get data from avalon knob
|
||||
avalon_knob_data = get_avalon_knob_data(node, ["avalon:", "ak:"])
|
||||
avalon_knob_data = read(node)
|
||||
|
||||
if not avalon_knob_data:
|
||||
continue
|
||||
|
|
@ -730,7 +805,7 @@ class WorkfileSettings(object):
|
|||
continue
|
||||
|
||||
# load nuke presets for Read's colorspace
|
||||
read_clrs_presets = get_colorspace_preset().get(
|
||||
read_clrs_presets = config.get_init_presets()["colorspace"].get(
|
||||
"nuke", {}).get("read", {})
|
||||
|
||||
# check if any colorspace presets for read is mathing
|
||||
|
|
@ -771,7 +846,8 @@ class WorkfileSettings(object):
|
|||
def set_colorspace(self):
|
||||
''' Setting colorpace following presets
|
||||
'''
|
||||
nuke_colorspace = get_colorspace_preset().get("nuke", None)
|
||||
nuke_colorspace = config.get_init_presets(
|
||||
)["colorspace"].get("nuke", None)
|
||||
|
||||
try:
|
||||
self.set_root_colorspace(nuke_colorspace["root"])
|
||||
|
|
@ -836,7 +912,7 @@ class WorkfileSettings(object):
|
|||
handle_start = data["handleStart"]
|
||||
handle_end = data["handleEnd"]
|
||||
|
||||
fps = data["fps"]
|
||||
fps = float(data["fps"])
|
||||
frame_start = int(data["frameStart"]) - handle_start
|
||||
frame_end = int(data["frameEnd"]) + handle_end
|
||||
|
||||
|
|
@ -863,7 +939,7 @@ class WorkfileSettings(object):
|
|||
node['frame_range_lock'].setValue(True)
|
||||
|
||||
# adding handle_start/end to root avalon knob
|
||||
if not avalon.nuke.imprint(self._root_node, {
|
||||
if not anlib.set_avalon_knob_data(self._root_node, {
|
||||
"handleStart": int(handle_start),
|
||||
"handleEnd": int(handle_end)
|
||||
}):
|
||||
|
|
@ -971,7 +1047,7 @@ class WorkfileSettings(object):
|
|||
# replace reset resolution from avalon core to pype's
|
||||
self.reset_frame_range_handles()
|
||||
# add colorspace menu item
|
||||
self.set_colorspace()
|
||||
# self.set_colorspace()
|
||||
|
||||
def set_favorites(self):
|
||||
work_dir = os.getenv("AVALON_WORKDIR")
|
||||
|
|
@ -1031,8 +1107,8 @@ def get_write_node_template_attr(node):
|
|||
'''
|
||||
# get avalon data from node
|
||||
data = dict()
|
||||
data['avalon'] = avalon.nuke.get_avalon_knob_data(
|
||||
node, ['avalon:', 'ak:'])
|
||||
data['avalon'] = avalon.nuke.read(
|
||||
node)
|
||||
data_preset = {
|
||||
"class": data['avalon']['family'],
|
||||
"families": data['avalon']['families'],
|
||||
|
|
@ -1040,25 +1116,20 @@ def get_write_node_template_attr(node):
|
|||
}
|
||||
|
||||
# get template data
|
||||
nuke_dataflow_writes = get_node_dataflow_preset(**data_preset)
|
||||
nuke_colorspace_writes = get_node_colorspace_preset(**data_preset)
|
||||
nuke_imageio_writes = get_node_imageio_setting(**data_preset)
|
||||
|
||||
# collecting correct data
|
||||
correct_data = OrderedDict({
|
||||
"file": get_render_path(node)
|
||||
})
|
||||
|
||||
# adding dataflow template
|
||||
# adding imageio template
|
||||
{correct_data.update({k: v})
|
||||
for k, v in nuke_dataflow_writes.items()
|
||||
for k, v in nuke_imageio_writes.items()
|
||||
if k not in ["_id", "_previous"]}
|
||||
|
||||
# adding colorspace template
|
||||
{correct_data.update({k: v})
|
||||
for k, v in nuke_colorspace_writes.items()}
|
||||
|
||||
# fix badly encoded data
|
||||
return avalon.nuke.lib.fix_data_for_node_create(correct_data)
|
||||
return anlib.fix_data_for_node_create(correct_data)
|
||||
|
||||
|
||||
class ExporterReview:
|
||||
|
|
@ -1177,6 +1248,7 @@ class ExporterReviewLut(ExporterReview):
|
|||
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
klass,
|
||||
instance,
|
||||
|
|
@ -1279,6 +1351,7 @@ class ExporterReviewMov(ExporterReview):
|
|||
instance (pyblish.instance): instance of pyblish context
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
klass,
|
||||
instance,
|
||||
|
|
@ -1298,6 +1371,7 @@ class ExporterReviewMov(ExporterReview):
|
|||
self.viewer_lut_raw = klass.viewer_lut_raw
|
||||
self.bake_colorspace_fallback = klass.bake_colorspace_fallback
|
||||
self.bake_colorspace_main = klass.bake_colorspace_main
|
||||
self.write_colorspace = instance.data["colorspace"]
|
||||
|
||||
self.name = name or "baked"
|
||||
self.ext = ext or "mov"
|
||||
|
|
@ -1343,6 +1417,8 @@ class ExporterReviewMov(ExporterReview):
|
|||
r_node["origfirst"].setValue(self.first_frame)
|
||||
r_node["last"].setValue(self.last_frame)
|
||||
r_node["origlast"].setValue(self.last_frame)
|
||||
r_node["colorspace"].setValue(self.write_colorspace)
|
||||
|
||||
# connect
|
||||
self._temp_nodes.append(r_node)
|
||||
self.previous_node = r_node
|
||||
|
|
@ -1,11 +1,8 @@
|
|||
import os
|
||||
import nuke
|
||||
from avalon.api import Session
|
||||
|
||||
from pype.hosts.nuke import lib
|
||||
from ...lib import BuildWorkfile
|
||||
from pype.api import Logger
|
||||
from pype.tools import workfiles
|
||||
from .lib import WorkfileSettings
|
||||
from pype.api import Logger, BuildWorkfile
|
||||
|
||||
log = Logger().get_logger(__name__)
|
||||
|
||||
|
|
@ -13,25 +10,6 @@ log = Logger().get_logger(__name__)
|
|||
def install():
|
||||
menubar = nuke.menu("Nuke")
|
||||
menu = menubar.findItem(Session["AVALON_LABEL"])
|
||||
workfile_settings = lib.WorkfileSettings
|
||||
|
||||
# replace reset resolution from avalon core to pype's
|
||||
name = "Work Files..."
|
||||
rm_item = [
|
||||
(i, item) for i, item in enumerate(menu.items()) if name in item.name()
|
||||
][0]
|
||||
|
||||
log.debug("Changing Item: {}".format(rm_item))
|
||||
|
||||
menu.removeItem(rm_item[1].name())
|
||||
menu.addCommand(
|
||||
name,
|
||||
lambda: workfiles.show(
|
||||
os.environ["AVALON_WORKDIR"]
|
||||
),
|
||||
index=(rm_item[0])
|
||||
)
|
||||
|
||||
# replace reset resolution from avalon core to pype's
|
||||
name = "Reset Resolution"
|
||||
new_name = "Set Resolution"
|
||||
|
|
@ -44,7 +22,7 @@ def install():
|
|||
menu.removeItem(rm_item[1].name())
|
||||
menu.addCommand(
|
||||
new_name,
|
||||
lambda: workfile_settings().reset_resolution(),
|
||||
lambda: WorkfileSettings().reset_resolution(),
|
||||
index=(rm_item[0])
|
||||
)
|
||||
|
||||
|
|
@ -59,14 +37,14 @@ def install():
|
|||
menu.removeItem(rm_item[1].name())
|
||||
menu.addCommand(
|
||||
new_name,
|
||||
lambda: workfile_settings().reset_frame_range_handles(),
|
||||
lambda: WorkfileSettings().reset_frame_range_handles(),
|
||||
index=(rm_item[0])
|
||||
)
|
||||
|
||||
# add colorspace menu item
|
||||
name = "Set Colorspace"
|
||||
menu.addCommand(
|
||||
name, lambda: workfile_settings().set_colorspace(),
|
||||
name, lambda: WorkfileSettings().set_colorspace(),
|
||||
index=(rm_item[0] + 2)
|
||||
)
|
||||
log.debug("Adding menu item: {}".format(name))
|
||||
|
|
@ -83,7 +61,7 @@ def install():
|
|||
name = "Apply All Settings"
|
||||
menu.addCommand(
|
||||
name,
|
||||
lambda: workfile_settings().set_context_settings(),
|
||||
lambda: WorkfileSettings().set_context_settings(),
|
||||
index=(rm_item[0] + 3)
|
||||
)
|
||||
log.debug("Adding menu item: {}".format(name))
|
||||
23
pype/hosts/nuke/api/plugin.py
Normal file
23
pype/hosts/nuke/api/plugin.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import avalon.api
|
||||
import avalon.nuke
|
||||
from pype.api import get_current_project_settings
|
||||
from .lib import check_subsetname_exists
|
||||
import nuke
|
||||
|
||||
|
||||
class PypeCreator(avalon.nuke.pipeline.Creator):
|
||||
"""Pype Nuke Creator class wrapper
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PypeCreator, self).__init__(*args, **kwargs)
|
||||
self.presets = get_current_project_settings()["nuke"]["create"].get(
|
||||
self.__class__.__name__, {}
|
||||
)
|
||||
if check_subsetname_exists(
|
||||
nuke.allNodes(),
|
||||
self.data["subset"]):
|
||||
msg = ("The subset name `{0}` is already used on a node in"
|
||||
"this workfile.".format(self.data["subset"]))
|
||||
self.log.error(msg + '\n\nPlease use other subset name!')
|
||||
raise NameError("`{0}: {1}".format(__name__, msg))
|
||||
return
|
||||
|
|
@ -4,12 +4,13 @@ from avalon.nuke import lib as anlib
|
|||
from pype.api import resources
|
||||
|
||||
|
||||
def set_context_favorites(favorites={}):
|
||||
def set_context_favorites(favorites=None):
|
||||
""" Addig favorite folders to nuke's browser
|
||||
|
||||
Argumets:
|
||||
favorites (dict): couples of {name:path}
|
||||
"""
|
||||
favorites = favorites or {}
|
||||
icon_path = resources.get_resource("icons", "folder-favorite3.png")
|
||||
for name, path in favorites.items():
|
||||
nuke.addFavoriteDir(
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
import re
|
||||
import avalon.api
|
||||
import avalon.nuke
|
||||
from pype.api import get_current_project_settings
|
||||
|
||||
class PypeCreator(avalon.nuke.pipeline.Creator):
|
||||
"""Pype Nuke Creator class wrapper
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PypeCreator, self).__init__(*args, **kwargs)
|
||||
self.presets = get_current_project_settings()["nuke"]["create"].get(
|
||||
self.__class__.__name__, {}
|
||||
)
|
||||
|
|
@ -32,7 +32,7 @@ class CreateBackdrop(avalon.nuke.Creator):
|
|||
bckd_node["note_font_size"].setValue(24)
|
||||
bckd_node["label"].setValue("[{}]".format(self.name))
|
||||
# add avalon knobs
|
||||
instance = anlib.imprint(bckd_node, self.data)
|
||||
instance = anlib.set_avalon_knob_data(bckd_node, self.data)
|
||||
|
||||
return instance
|
||||
else:
|
||||
|
|
@ -48,6 +48,6 @@ class CreateBackdrop(avalon.nuke.Creator):
|
|||
bckd_node["note_font_size"].setValue(24)
|
||||
bckd_node["label"].setValue("[{}]".format(self.name))
|
||||
# add avalon knobs
|
||||
instance = anlib.imprint(bckd_node, self.data)
|
||||
instance = anlib.set_avalon_knob_data(bckd_node, self.data)
|
||||
|
||||
return instance
|
||||
|
|
@ -36,7 +36,7 @@ class CreateCamera(avalon.nuke.Creator):
|
|||
# change node color
|
||||
n["tile_color"].setValue(int(self.node_color, 16))
|
||||
# add avalon knobs
|
||||
anlib.imprint(n, data)
|
||||
anlib.set_avalon_knob_data(n, data)
|
||||
return True
|
||||
else:
|
||||
msg = str("Please select nodes you "
|
||||
|
|
@ -49,5 +49,5 @@ class CreateCamera(avalon.nuke.Creator):
|
|||
camera_node = nuke.createNode("Camera2")
|
||||
camera_node["tile_color"].setValue(int(self.node_color, 16))
|
||||
# add avalon knobs
|
||||
instance = anlib.imprint(camera_node, self.data)
|
||||
instance = anlib.set_avalon_knob_data(camera_node, self.data)
|
||||
return instance
|
||||
|
|
@ -34,7 +34,7 @@ class CreateGizmo(avalon.nuke.Creator):
|
|||
if node.Class() in "Group":
|
||||
node["name"].setValue("{}_GZM".format(self.name))
|
||||
node["tile_color"].setValue(int(self.node_color, 16))
|
||||
return anlib.imprint(node, self.data)
|
||||
return anlib.set_avalon_knob_data(node, self.data)
|
||||
else:
|
||||
msg = ("Please select a group node "
|
||||
"you wish to publish as the gizmo")
|
||||
|
|
@ -57,7 +57,7 @@ class CreateGizmo(avalon.nuke.Creator):
|
|||
"- create User knobs on the group")
|
||||
|
||||
# add avalon knobs
|
||||
return anlib.imprint(gizmo_node, self.data)
|
||||
return anlib.set_avalon_knob_data(gizmo_node, self.data)
|
||||
|
||||
else:
|
||||
msg = ("Please select nodes you "
|
||||
|
|
@ -80,4 +80,4 @@ class CreateGizmo(avalon.nuke.Creator):
|
|||
"- create User knobs on the group")
|
||||
|
||||
# add avalon knobs
|
||||
return anlib.imprint(gizmo_node, self.data)
|
||||
return anlib.set_avalon_knob_data(gizmo_node, self.data)
|
||||
|
|
@ -44,7 +44,8 @@ class CrateRead(avalon.nuke.Creator):
|
|||
continue
|
||||
avalon_data = self.data
|
||||
avalon_data['subset'] = "{}".format(self.name)
|
||||
self.change_read_node(self.data["subset"], node, avalon_data)
|
||||
avalon.nuke.lib.set_avalon_knob_data(node, avalon_data)
|
||||
node['tile_color'].setValue(16744935)
|
||||
count_reads += 1
|
||||
|
||||
if count_reads < 1:
|
||||
|
|
@ -52,7 +53,3 @@ class CrateRead(avalon.nuke.Creator):
|
|||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
return
|
||||
|
||||
def change_read_node(self, name, node, data):
|
||||
node = avalon.nuke.lib.imprint(node, data)
|
||||
node['tile_color'].setValue(16744935)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from collections import OrderedDict
|
||||
from pype.hosts.nuke import (
|
||||
from pype.hosts.nuke.api import (
|
||||
plugin,
|
||||
lib as pnlib)
|
||||
lib)
|
||||
import nuke
|
||||
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ class CreateWritePrerender(plugin.PypeCreator):
|
|||
name = "WritePrerender"
|
||||
label = "Create Write Prerender"
|
||||
hosts = ["nuke"]
|
||||
n_class = "write"
|
||||
n_class = "Write"
|
||||
family = "prerender"
|
||||
icon = "sign-out"
|
||||
defaults = ["Key01", "Bg01", "Fg01", "Branch01", "Part01"]
|
||||
|
|
@ -75,9 +75,10 @@ class CreateWritePrerender(plugin.PypeCreator):
|
|||
|
||||
# recreate new
|
||||
write_data = {
|
||||
"class": self.n_class,
|
||||
"nodeclass": self.n_class,
|
||||
"families": [self.family],
|
||||
"avalon": self.data
|
||||
"avalon": self.data,
|
||||
"creator": self.__class__.__name__
|
||||
}
|
||||
|
||||
if self.presets.get('fpath_template'):
|
||||
|
|
@ -91,7 +92,9 @@ class CreateWritePrerender(plugin.PypeCreator):
|
|||
"fpath_template": ("{work}/prerenders/nuke/{subset}"
|
||||
"/{subset}.{frame}.{ext}")})
|
||||
|
||||
write_node = pnlib.create_write_node(
|
||||
self.log.info("write_data: {}".format(write_data))
|
||||
|
||||
write_node = lib.create_write_node(
|
||||
self.data["subset"],
|
||||
write_data,
|
||||
input=selected_node,
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from collections import OrderedDict
|
||||
from pype.hosts.nuke import (
|
||||
from pype.hosts.nuke.api import (
|
||||
plugin,
|
||||
lib as pnlib)
|
||||
lib)
|
||||
import nuke
|
||||
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ class CreateWriteRender(plugin.PypeCreator):
|
|||
name = "WriteRender"
|
||||
label = "Create Write Render"
|
||||
hosts = ["nuke"]
|
||||
n_class = "write"
|
||||
n_class = "Write"
|
||||
family = "render"
|
||||
icon = "sign-out"
|
||||
defaults = ["Main", "Mask"]
|
||||
|
|
@ -48,6 +48,7 @@ class CreateWriteRender(plugin.PypeCreator):
|
|||
"or tick off `Use selection`")
|
||||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
return
|
||||
|
||||
if len(nodes) == 0:
|
||||
msg = (
|
||||
|
|
@ -56,6 +57,7 @@ class CreateWriteRender(plugin.PypeCreator):
|
|||
)
|
||||
self.log.error(msg)
|
||||
nuke.message(msg)
|
||||
return
|
||||
|
||||
selected_node = nodes[0]
|
||||
inputs = [selected_node]
|
||||
|
|
@ -76,9 +78,10 @@ class CreateWriteRender(plugin.PypeCreator):
|
|||
|
||||
# recreate new
|
||||
write_data = {
|
||||
"class": self.n_class,
|
||||
"nodeclass": self.n_class,
|
||||
"families": [self.family],
|
||||
"avalon": self.data
|
||||
"avalon": self.data,
|
||||
"creator": self.__class__.__name__
|
||||
}
|
||||
|
||||
if self.presets.get('fpath_template'):
|
||||
|
|
@ -92,7 +95,7 @@ class CreateWriteRender(plugin.PypeCreator):
|
|||
"fpath_template": ("{work}/renders/nuke/{subset}"
|
||||
"/{subset}.{frame}.{ext}")})
|
||||
|
||||
write_node = pnlib.create_write_node(
|
||||
write_node = lib.create_write_node(
|
||||
self.data["subset"],
|
||||
write_data,
|
||||
input=selected_node)
|
||||
|
|
@ -25,7 +25,7 @@ class SetFrameRangeLoader(api.Loader):
|
|||
|
||||
def load(self, context, name, namespace, data):
|
||||
|
||||
from pype.hosts.nuke import lib
|
||||
from pype.hosts.nuke.api import lib
|
||||
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
|
|
@ -59,7 +59,7 @@ class SetFrameRangeWithHandlesLoader(api.Loader):
|
|||
|
||||
def load(self, context, name, namespace, data):
|
||||
|
||||
from pype.hosts.nuke import lib
|
||||
from pype.hosts.nuke.api import lib
|
||||
|
||||
version = context['version']
|
||||
version_data = version.get("data", {})
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from avalon import api, style, io
|
||||
import nuke
|
||||
import nukescripts
|
||||
from pype.hosts.nuke import lib as pnlib
|
||||
from pype.hosts.nuke.api import lib as pnlib
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon.nuke import containerise, update_container
|
||||
reload(pnlib)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
from avalon import api, style, io
|
||||
import nuke
|
||||
from pype.hosts.nuke import lib as pnlib
|
||||
from pype.hosts.nuke.api import lib as pnlib
|
||||
from avalon.nuke import lib as anlib
|
||||
from avalon.nuke import containerise, update_container
|
||||
|
||||
|
|
@ -4,7 +4,9 @@ import nuke
|
|||
from avalon.vendor import qargparse
|
||||
from avalon import api, io
|
||||
|
||||
from pype.hosts.nuke import presets
|
||||
from pype.hosts.nuke.api.lib import (
|
||||
get_imageio_input_colorspace
|
||||
)
|
||||
|
||||
|
||||
class LoadImage(api.Loader):
|
||||
|
|
@ -90,17 +92,10 @@ class LoadImage(api.Loader):
|
|||
if colorspace:
|
||||
r["colorspace"].setValue(str(colorspace))
|
||||
|
||||
# load nuke presets for Read's colorspace
|
||||
read_clrs_presets = presets.get_colorspace_preset().get(
|
||||
"nuke", {}).get("read", {})
|
||||
preset_clrsp = get_imageio_input_colorspace(file)
|
||||
|
||||
# check if any colorspace presets for read is mathing
|
||||
preset_clrsp = next((read_clrs_presets[k]
|
||||
for k in read_clrs_presets
|
||||
if bool(re.search(k, file))),
|
||||
None)
|
||||
if preset_clrsp is not None:
|
||||
r["colorspace"].setValue(str(preset_clrsp))
|
||||
r["colorspace"].setValue(preset_clrsp)
|
||||
|
||||
r["origfirst"].setValue(first)
|
||||
r["first"].setValue(first)
|
||||
|
|
@ -2,7 +2,7 @@ from avalon import api, style, io
|
|||
import nuke
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from pype.hosts.nuke import lib
|
||||
from pype.hosts.nuke.api import lib
|
||||
|
||||
|
||||
class LoadLutsInputProcess(api.Loader):
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
import re
|
||||
import nuke
|
||||
import contextlib
|
||||
|
||||
from avalon import api, io
|
||||
from pype.hosts.nuke import presets
|
||||
from pype.api import get_project_settings
|
||||
from pype.api import get_current_project_settings
|
||||
from pype.hosts.nuke.api.lib import (
|
||||
get_imageio_input_colorspace
|
||||
)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
|
|
@ -73,12 +74,18 @@ def add_review_presets_config():
|
|||
"families": list(),
|
||||
"representations": list()
|
||||
}
|
||||
settings = get_project_settings(io.Session["AVALON_PROJECT"])
|
||||
review_presets = settings["global"]["publish"].get(
|
||||
"ExtractReview", {})
|
||||
settings = get_current_project_settings()
|
||||
review_profiles = (
|
||||
settings["global"]
|
||||
["publish"]
|
||||
["ExtractReview"]
|
||||
["profiles"]
|
||||
)
|
||||
|
||||
outputs = {}
|
||||
for profile in review_profiles:
|
||||
outputs.update(profile.get("outputs", {}))
|
||||
|
||||
outputs = review_presets.get("outputs", {})
|
||||
#
|
||||
for output, properities in outputs.items():
|
||||
returning["representations"].append(output)
|
||||
returning["families"] += properities.get("families", [])
|
||||
|
|
@ -175,17 +182,10 @@ class LoadMov(api.Loader):
|
|||
if colorspace:
|
||||
read_node["colorspace"].setValue(str(colorspace))
|
||||
|
||||
# load nuke presets for Read's colorspace
|
||||
read_clrs_presets = presets.get_colorspace_preset().get(
|
||||
"nuke", {}).get("read", {})
|
||||
preset_clrsp = get_imageio_input_colorspace(file)
|
||||
|
||||
# check if any colorspace presets for read is mathing
|
||||
preset_clrsp = next((read_clrs_presets[k]
|
||||
for k in read_clrs_presets
|
||||
if bool(re.search(k, file))),
|
||||
None)
|
||||
if preset_clrsp is not None:
|
||||
read_node["colorspace"].setValue(str(preset_clrsp))
|
||||
read_node["colorspace"].setValue(preset_clrsp)
|
||||
|
||||
# add additional metadata from the version to imprint Avalon knob
|
||||
add_keys = [
|
||||
|
|
@ -276,10 +276,11 @@ class LoadMov(api.Loader):
|
|||
colorspace = version_data.get("colorspace")
|
||||
|
||||
if first is None:
|
||||
self.log.warning("Missing start frame for updated version"
|
||||
"assuming starts at frame 0 for: "
|
||||
"{} ({})".format(
|
||||
node['name'].value(), representation))
|
||||
self.log.warning((
|
||||
"Missing start frame for updated version"
|
||||
"assuming starts at frame 0 for: "
|
||||
"{} ({})").format(
|
||||
node['name'].value(), representation))
|
||||
first = 0
|
||||
|
||||
# fix handle start and end if none are available
|
||||
|
|
@ -309,17 +310,10 @@ class LoadMov(api.Loader):
|
|||
if colorspace:
|
||||
node["colorspace"].setValue(str(colorspace))
|
||||
|
||||
# load nuke presets for Read's colorspace
|
||||
read_clrs_presets = presets.get_colorspace_preset().get(
|
||||
"nuke", {}).get("read", {})
|
||||
preset_clrsp = get_imageio_input_colorspace(file)
|
||||
|
||||
# check if any colorspace presets for read is mathing
|
||||
preset_clrsp = next((read_clrs_presets[k]
|
||||
for k in read_clrs_presets
|
||||
if bool(re.search(k, file))),
|
||||
None)
|
||||
if preset_clrsp is not None:
|
||||
node["colorspace"].setValue(str(preset_clrsp))
|
||||
node["colorspace"].setValue(preset_clrsp)
|
||||
|
||||
updated_dict = {}
|
||||
updated_dict.update({
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue