├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.rst ├── docs ├── Makefile ├── make.bat └── source │ ├── advanced_use.rst │ ├── api_documentation.rst │ ├── conf.py │ ├── getting_started.rst │ ├── index.rst │ └── installation.rst ├── nukecontexts ├── __init__.py └── ctx.py ├── run_tests.py ├── setup.cfg ├── setup.py └── tests ├── conftest.py └── test_contexts.py /.gitignore: -------------------------------------------------------------------------------- 1 | .tox/* 2 | .idea/* 3 | .cache/* 4 | __pycache__/* 5 | bin/* 6 | docs/build/* 7 | docs/_build/* 8 | tests/assets/production/* 9 | *.egg 10 | *.egg-info 11 | *.log 12 | *.pyc 13 | *.pyd 14 | *.pyo 15 | .DS_Store 16 | .coverage 17 | dist 18 | build 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Florian Einfalt 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include .gitignore 2 | include LICENSE 3 | include README.rst 4 | 5 | graft tests 6 | graft docs 7 | 8 | global-exclude *.py[cod] 9 | 10 | prune docs/build 11 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | nukecontexts 2 | ============ 3 | 4 | .. image:: https://img.shields.io/pypi/l/nukecontexts.svg 5 | :target: https://pypi.org/project/nukecontexts/ 6 | .. image:: https://img.shields.io/pypi/pyversions/nukecontexts.svg 7 | :target: https://pypi.org/project/nukecontexts/ 8 | .. image:: https://img.shields.io/pypi/v/nukecontexts.svg 9 | :target: https://pypi.org/project/nukecontexts/ 10 | .. image:: https://img.shields.io/pypi/wheel/nukecontexts.svg 11 | :target: https://pypi.org/project/nukecontexts/ 12 | .. image:: https://readthedocs.org/projects/nukecontexts/badge/?version=latest 13 | :target: https://readthedocs.org/projects/nukecontexts/?badge=latest 14 | 15 | ``nukecontexts`` is a library of composable context managers for Nuke to manage the state of complex compositing scripts in code. 16 | 17 | `Full Documentation`_ 18 | 19 | Installation 20 | ------------ 21 | 22 | To install ``nukecontexts``, type: 23 | 24 | .. code-block:: bash 25 | 26 | $ pip install nukecontexts 27 | 28 | 29 | Open Nuke's ``init.py`` file and add: 30 | 31 | .. code-block:: python 32 | 33 | nuke.pluginAddPath('/path/to/your/local/python/site-packages') 34 | 35 | Getting Started 36 | --------------- 37 | 38 | The most common use case for ``nukecontexts`` is automated rendering of 39 | multiple states of a compositing script. For example two different output 40 | formats, jpg and png. 41 | 42 | .. code:: python 43 | 44 | import nuke 45 | import contextlib 46 | from nukecontexts import ctx 47 | 48 | render_node = nuke.toNode('Write1') 49 | with ctx.set_attr(render_node, 'file_type', 'jpeg'): 50 | nuke.execute(render_node.name(), 1, 1, 1) 51 | with ctx.set_attr(render_node, 'file_type', 'png'): 52 | nuke.execute(render_node.name(), 1, 1, 1) 53 | 54 | The power of ``nukecontexts`` comes with composable contexts, using 55 | ``contextlib.nested()``. Arbitrarily complex, varying states of the 56 | compositing script can be defined and used to automatically generate 57 | different results. 58 | 59 | .. code:: python 60 | 61 | merge_node = nuke.toNode('Merge1') 62 | grade_node = nuke.toNode('Grade1') 63 | switch_node = nuke.toNode('Switch1') 64 | 65 | ctx1 = ctx.enable([merge_node, grade_node]) 66 | ctx2 = ctx.set_attr(grade_node, 'white', 2.0) 67 | ctx3 = ctx.set_attr(switch_node, 'which', 0) 68 | ctx4 = ctx.disable(merge_node) 69 | 70 | with contextlib.nested(ctx1, ctx2, ctx3): 71 | """Render with the merge_node and grade_node enabled, the 72 | grade_node's white attribute set to 2.0 and the switch_node's switch 73 | position set to 0.""" 74 | nuke.execute(render_node.name(), 1, 1, 1) 75 | 76 | with contextlib.nested(ctx3, ctx4): 77 | """Render with the switch_node's switch position set to 0 and the 78 | merge node disabled; the grade_node's gain value remains at the 79 | original value.""" 80 | nuke.execute(render_node.name(), 1, 1, 1) 81 | 82 | .. _Full Documentation: http://nukecontexts.readthedocs.io/en/latest/ 83 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 16 | 17 | .PHONY: help 18 | help: 19 | @echo "Please use \`make ' where is one of" 20 | @echo " html to make standalone HTML files" 21 | @echo " dirhtml to make HTML files named index.html in directories" 22 | @echo " singlehtml to make a single large HTML file" 23 | @echo " pickle to make pickle files" 24 | @echo " json to make JSON files" 25 | @echo " htmlhelp to make HTML files and a HTML help project" 26 | @echo " qthelp to make HTML files and a qthelp project" 27 | @echo " applehelp to make an Apple Help Book" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " epub3 to make an epub3" 31 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 32 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 33 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 34 | @echo " text to make text files" 35 | @echo " man to make manual pages" 36 | @echo " texinfo to make Texinfo files" 37 | @echo " info to make Texinfo files and run them through makeinfo" 38 | @echo " gettext to make PO message catalogs" 39 | @echo " changes to make an overview of all changed/added/deprecated items" 40 | @echo " xml to make Docutils-native XML files" 41 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 42 | @echo " linkcheck to check all external links for integrity" 43 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 44 | @echo " coverage to run coverage check of the documentation (if enabled)" 45 | @echo " dummy to check syntax errors of document sources" 46 | 47 | .PHONY: clean 48 | clean: 49 | rm -rf $(BUILDDIR)/* 50 | 51 | .PHONY: html 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | .PHONY: dirhtml 58 | dirhtml: 59 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 60 | @echo 61 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 62 | 63 | .PHONY: singlehtml 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | .PHONY: pickle 70 | pickle: 71 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 72 | @echo 73 | @echo "Build finished; now you can process the pickle files." 74 | 75 | .PHONY: json 76 | json: 77 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 78 | @echo 79 | @echo "Build finished; now you can process the JSON files." 80 | 81 | .PHONY: htmlhelp 82 | htmlhelp: 83 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 84 | @echo 85 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 86 | ".hhp project file in $(BUILDDIR)/htmlhelp." 87 | 88 | .PHONY: qthelp 89 | qthelp: 90 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 91 | @echo 92 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 93 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 94 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/nukecontexts.qhcp" 95 | @echo "To view the help file:" 96 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/nukecontexts.qhc" 97 | 98 | .PHONY: applehelp 99 | applehelp: 100 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 101 | @echo 102 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 103 | @echo "N.B. You won't be able to view it unless you put it in" \ 104 | "~/Library/Documentation/Help or install it in your application" \ 105 | "bundle." 106 | 107 | .PHONY: devhelp 108 | devhelp: 109 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 110 | @echo 111 | @echo "Build finished." 112 | @echo "To view the help file:" 113 | @echo "# mkdir -p $$HOME/.local/share/devhelp/nukecontexts" 114 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/nukecontexts" 115 | @echo "# devhelp" 116 | 117 | .PHONY: epub 118 | epub: 119 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 120 | @echo 121 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 122 | 123 | .PHONY: epub3 124 | epub3: 125 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 126 | @echo 127 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." 128 | 129 | .PHONY: latex 130 | latex: 131 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 132 | @echo 133 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 134 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 135 | "(use \`make latexpdf' here to do that automatically)." 136 | 137 | .PHONY: latexpdf 138 | latexpdf: 139 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 140 | @echo "Running LaTeX files through pdflatex..." 141 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 142 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 143 | 144 | .PHONY: latexpdfja 145 | latexpdfja: 146 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 147 | @echo "Running LaTeX files through platex and dvipdfmx..." 148 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 149 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 150 | 151 | .PHONY: text 152 | text: 153 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 154 | @echo 155 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 156 | 157 | .PHONY: man 158 | man: 159 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 160 | @echo 161 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 162 | 163 | .PHONY: texinfo 164 | texinfo: 165 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 166 | @echo 167 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 168 | @echo "Run \`make' in that directory to run these through makeinfo" \ 169 | "(use \`make info' here to do that automatically)." 170 | 171 | .PHONY: info 172 | info: 173 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 174 | @echo "Running Texinfo files through makeinfo..." 175 | make -C $(BUILDDIR)/texinfo info 176 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 177 | 178 | .PHONY: gettext 179 | gettext: 180 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 181 | @echo 182 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 183 | 184 | .PHONY: changes 185 | changes: 186 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 187 | @echo 188 | @echo "The overview file is in $(BUILDDIR)/changes." 189 | 190 | .PHONY: linkcheck 191 | linkcheck: 192 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 193 | @echo 194 | @echo "Link check complete; look for any errors in the above output " \ 195 | "or in $(BUILDDIR)/linkcheck/output.txt." 196 | 197 | .PHONY: doctest 198 | doctest: 199 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 200 | @echo "Testing of doctests in the sources finished, look at the " \ 201 | "results in $(BUILDDIR)/doctest/output.txt." 202 | 203 | .PHONY: coverage 204 | coverage: 205 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 206 | @echo "Testing of coverage in the sources finished, look at the " \ 207 | "results in $(BUILDDIR)/coverage/python.txt." 208 | 209 | .PHONY: xml 210 | xml: 211 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 212 | @echo 213 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 214 | 215 | .PHONY: pseudoxml 216 | pseudoxml: 217 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 218 | @echo 219 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 220 | 221 | .PHONY: dummy 222 | dummy: 223 | $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy 224 | @echo 225 | @echo "Build finished. Dummy builder generates no files." 226 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. epub3 to make an epub3 31 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 32 | echo. text to make text files 33 | echo. man to make manual pages 34 | echo. texinfo to make Texinfo files 35 | echo. gettext to make PO message catalogs 36 | echo. changes to make an overview over all changed/added/deprecated items 37 | echo. xml to make Docutils-native XML files 38 | echo. pseudoxml to make pseudoxml-XML files for display purposes 39 | echo. linkcheck to check all external links for integrity 40 | echo. doctest to run all doctests embedded in the documentation if enabled 41 | echo. coverage to run coverage check of the documentation if enabled 42 | echo. dummy to check syntax errors of document sources 43 | goto end 44 | ) 45 | 46 | if "%1" == "clean" ( 47 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 48 | del /q /s %BUILDDIR%\* 49 | goto end 50 | ) 51 | 52 | 53 | REM Check if sphinx-build is available and fallback to Python version if any 54 | %SPHINXBUILD% 1>NUL 2>NUL 55 | if errorlevel 9009 goto sphinx_python 56 | goto sphinx_ok 57 | 58 | :sphinx_python 59 | 60 | set SPHINXBUILD=python -m sphinx.__init__ 61 | %SPHINXBUILD% 2> nul 62 | if errorlevel 9009 ( 63 | echo. 64 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 65 | echo.installed, then set the SPHINXBUILD environment variable to point 66 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 67 | echo.may add the Sphinx directory to PATH. 68 | echo. 69 | echo.If you don't have Sphinx installed, grab it from 70 | echo.http://sphinx-doc.org/ 71 | exit /b 1 72 | ) 73 | 74 | :sphinx_ok 75 | 76 | 77 | if "%1" == "html" ( 78 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 79 | if errorlevel 1 exit /b 1 80 | echo. 81 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 82 | goto end 83 | ) 84 | 85 | if "%1" == "dirhtml" ( 86 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 87 | if errorlevel 1 exit /b 1 88 | echo. 89 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 90 | goto end 91 | ) 92 | 93 | if "%1" == "singlehtml" ( 94 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 95 | if errorlevel 1 exit /b 1 96 | echo. 97 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 98 | goto end 99 | ) 100 | 101 | if "%1" == "pickle" ( 102 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 103 | if errorlevel 1 exit /b 1 104 | echo. 105 | echo.Build finished; now you can process the pickle files. 106 | goto end 107 | ) 108 | 109 | if "%1" == "json" ( 110 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 111 | if errorlevel 1 exit /b 1 112 | echo. 113 | echo.Build finished; now you can process the JSON files. 114 | goto end 115 | ) 116 | 117 | if "%1" == "htmlhelp" ( 118 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 119 | if errorlevel 1 exit /b 1 120 | echo. 121 | echo.Build finished; now you can run HTML Help Workshop with the ^ 122 | .hhp project file in %BUILDDIR%/htmlhelp. 123 | goto end 124 | ) 125 | 126 | if "%1" == "qthelp" ( 127 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 128 | if errorlevel 1 exit /b 1 129 | echo. 130 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 131 | .qhcp project file in %BUILDDIR%/qthelp, like this: 132 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\nukecontexts.qhcp 133 | echo.To view the help file: 134 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\nukecontexts.ghc 135 | goto end 136 | ) 137 | 138 | if "%1" == "devhelp" ( 139 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 140 | if errorlevel 1 exit /b 1 141 | echo. 142 | echo.Build finished. 143 | goto end 144 | ) 145 | 146 | if "%1" == "epub" ( 147 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 148 | if errorlevel 1 exit /b 1 149 | echo. 150 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 151 | goto end 152 | ) 153 | 154 | if "%1" == "epub3" ( 155 | %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 156 | if errorlevel 1 exit /b 1 157 | echo. 158 | echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. 159 | goto end 160 | ) 161 | 162 | if "%1" == "latex" ( 163 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 164 | if errorlevel 1 exit /b 1 165 | echo. 166 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdf" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "latexpdfja" ( 181 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 182 | cd %BUILDDIR%/latex 183 | make all-pdf-ja 184 | cd %~dp0 185 | echo. 186 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 187 | goto end 188 | ) 189 | 190 | if "%1" == "text" ( 191 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 192 | if errorlevel 1 exit /b 1 193 | echo. 194 | echo.Build finished. The text files are in %BUILDDIR%/text. 195 | goto end 196 | ) 197 | 198 | if "%1" == "man" ( 199 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 200 | if errorlevel 1 exit /b 1 201 | echo. 202 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 203 | goto end 204 | ) 205 | 206 | if "%1" == "texinfo" ( 207 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 208 | if errorlevel 1 exit /b 1 209 | echo. 210 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 211 | goto end 212 | ) 213 | 214 | if "%1" == "gettext" ( 215 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 216 | if errorlevel 1 exit /b 1 217 | echo. 218 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 219 | goto end 220 | ) 221 | 222 | if "%1" == "changes" ( 223 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 224 | if errorlevel 1 exit /b 1 225 | echo. 226 | echo.The overview file is in %BUILDDIR%/changes. 227 | goto end 228 | ) 229 | 230 | if "%1" == "linkcheck" ( 231 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 232 | if errorlevel 1 exit /b 1 233 | echo. 234 | echo.Link check complete; look for any errors in the above output ^ 235 | or in %BUILDDIR%/linkcheck/output.txt. 236 | goto end 237 | ) 238 | 239 | if "%1" == "doctest" ( 240 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 241 | if errorlevel 1 exit /b 1 242 | echo. 243 | echo.Testing of doctests in the sources finished, look at the ^ 244 | results in %BUILDDIR%/doctest/output.txt. 245 | goto end 246 | ) 247 | 248 | if "%1" == "coverage" ( 249 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 250 | if errorlevel 1 exit /b 1 251 | echo. 252 | echo.Testing of coverage in the sources finished, look at the ^ 253 | results in %BUILDDIR%/coverage/python.txt. 254 | goto end 255 | ) 256 | 257 | if "%1" == "xml" ( 258 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 259 | if errorlevel 1 exit /b 1 260 | echo. 261 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 262 | goto end 263 | ) 264 | 265 | if "%1" == "pseudoxml" ( 266 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 267 | if errorlevel 1 exit /b 1 268 | echo. 269 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 270 | goto end 271 | ) 272 | 273 | if "%1" == "dummy" ( 274 | %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy 275 | if errorlevel 1 exit /b 1 276 | echo. 277 | echo.Build finished. Dummy builder generates no files. 278 | goto end 279 | ) 280 | 281 | :end 282 | -------------------------------------------------------------------------------- /docs/source/advanced_use.rst: -------------------------------------------------------------------------------- 1 | Advanced Use 2 | ============ 3 | 4 | Logging 5 | ------- 6 | 7 | ``nukecontexts`` creates it's own logger on import that is used by all context 8 | managers to indicate when contexts are entered and exited. The standard logger 9 | logs to ``stdout``. 10 | 11 | Should your pipeline have more advanced logging needs, simply pass your custom 12 | logger to each context manager, using the ``log`` keyword argument. 13 | 14 | Sentry support 15 | -------------- 16 | 17 | ``nukecontexts`` offers optional support for the `Sentry `_ 18 | error tracking service. To use `Sentry `_ with 19 | ``nukecontexts``, install `Raven `_ into 20 | your environment, set the ``SENTRY_DSN`` environment variable before importing 21 | ``nukecontexts`` and you're good to go. 22 | -------------------------------------------------------------------------------- /docs/source/api_documentation.rst: -------------------------------------------------------------------------------- 1 | API Documentation 2 | ================= 3 | 4 | .. automodule:: nukecontexts.ctx 5 | :members: 6 | :exclude-members: Progress, AttributeSetter 7 | 8 | .. autoclass:: nukecontexts.ctx.Progress 9 | :special-members: __init__ 10 | :members: 11 | 12 | .. autoclass:: nukecontexts.ctx.AttributeSetter 13 | :special-members: __init__ 14 | :members: 15 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # nukecontexts documentation build configuration file, created by 4 | # sphinx-quickstart on Thu Feb 9 15:53:41 2017. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | # 19 | # import os 20 | # import sys 21 | # sys.path.insert(0, os.path.abspath('.')) 22 | import os 23 | import sys 24 | import sphinx_rtd_theme 25 | os.environ['NON_PRODUCTION_CONTEXT'] = '1' 26 | 27 | cwd = os.getcwd() 28 | project_root = os.path.dirname(cwd) 29 | sys.path.insert(0, project_root) 30 | import nukecontexts 31 | 32 | # -- General configuration ------------------------------------------------ 33 | 34 | # If your documentation needs a minimal Sphinx version, state it here. 35 | # 36 | # needs_sphinx = '1.0' 37 | 38 | # Add any Sphinx extension module names here, as strings. They can be 39 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 40 | # ones. 41 | extensions = [ 42 | 'sphinx.ext.autodoc', 43 | 'sphinx.ext.doctest', 44 | 'sphinx.ext.intersphinx', 45 | 'sphinx.ext.viewcode', 46 | ] 47 | 48 | # Add any paths that contain templates here, relative to this directory. 49 | templates_path = ['_templates'] 50 | 51 | # The suffix(es) of source filenames. 52 | # You can specify multiple suffix as a list of string: 53 | # 54 | # source_suffix = ['.rst', '.md'] 55 | source_suffix = '.rst' 56 | 57 | # The encoding of source files. 58 | # 59 | # source_encoding = 'utf-8-sig' 60 | 61 | # The master toctree document. 62 | master_doc = 'index' 63 | 64 | # General information about the project. 65 | project = u'nukecontexts' 66 | copyright = u'2017, Florian Einfalt' 67 | author = u'Florian Einfalt' 68 | 69 | # The version info for the project you're documenting, acts as replacement for 70 | # |version| and |release|, also used in various other places throughout the 71 | # built documents. 72 | # 73 | # The short X.Y version. 74 | version = nukecontexts.__version__ 75 | # The full version, including alpha/beta/rc tags. 76 | release = nukecontexts.__version__ 77 | 78 | # The language for content autogenerated by Sphinx. Refer to documentation 79 | # for a list of supported languages. 80 | # 81 | # This is also used if you do content translation via gettext catalogs. 82 | # Usually you set "language" from the command line for these cases. 83 | language = None 84 | 85 | # There are two options for replacing |today|: either, you set today to some 86 | # non-false value, then it is used: 87 | # 88 | # today = '' 89 | # 90 | # Else, today_fmt is used as the format for a strftime call. 91 | # 92 | # today_fmt = '%B %d, %Y' 93 | 94 | # List of patterns, relative to source directory, that match files and 95 | # directories to ignore when looking for source files. 96 | # This patterns also effect to html_static_path and html_extra_path 97 | exclude_patterns = [] 98 | 99 | # The reST default role (used for this markup: `text`) to use for all 100 | # documents. 101 | # 102 | # default_role = None 103 | 104 | # If true, '()' will be appended to :func: etc. cross-reference text. 105 | # 106 | # add_function_parentheses = True 107 | 108 | # If true, the current module name will be prepended to all description 109 | # unit titles (such as .. function::). 110 | # 111 | # add_module_names = True 112 | 113 | # If true, sectionauthor and moduleauthor directives will be shown in the 114 | # output. They are ignored by default. 115 | # 116 | # show_authors = False 117 | 118 | # The name of the Pygments (syntax highlighting) style to use. 119 | pygments_style = 'sphinx' 120 | 121 | # A list of ignored prefixes for module index sorting. 122 | # modindex_common_prefix = [] 123 | 124 | # If true, keep warnings as "system message" paragraphs in the built documents. 125 | # keep_warnings = False 126 | 127 | # If true, `todo` and `todoList` produce output, else they produce nothing. 128 | todo_include_todos = False 129 | 130 | 131 | # -- Options for HTML output ---------------------------------------------- 132 | 133 | # The theme to use for HTML and HTML Help pages. See the documentation for 134 | # a list of builtin themes. 135 | # 136 | #html_theme = 'alabaster' 137 | html_theme = 'sphinx_rtd_theme' 138 | 139 | # Theme options are theme-specific and customize the look and feel of a theme 140 | # further. For a list of options available for each theme, see the 141 | # documentation. 142 | # 143 | # html_theme_options = {} 144 | 145 | # Add any paths that contain custom themes here, relative to this directory. 146 | # html_theme_path = [] 147 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 148 | 149 | # The name for this set of Sphinx documents. 150 | # " v documentation" by default. 151 | # 152 | # html_title = u'nukecontexts v0.1' 153 | 154 | # A shorter title for the navigation bar. Default is the same as html_title. 155 | # 156 | # html_short_title = None 157 | 158 | # The name of an image file (relative to this directory) to place at the top 159 | # of the sidebar. 160 | # 161 | # html_logo = None 162 | 163 | # The name of an image file (relative to this directory) to use as a favicon of 164 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 165 | # pixels large. 166 | # 167 | # html_favicon = None 168 | 169 | # Add any paths that contain custom static files (such as style sheets) here, 170 | # relative to this directory. They are copied after the builtin static files, 171 | # so a file named "default.css" will overwrite the builtin "default.css". 172 | html_static_path = ['_static'] 173 | 174 | # Add any extra paths that contain custom files (such as robots.txt or 175 | # .htaccess) here, relative to this directory. These files are copied 176 | # directly to the root of the documentation. 177 | # 178 | # html_extra_path = [] 179 | 180 | # If not None, a 'Last updated on:' timestamp is inserted at every page 181 | # bottom, using the given strftime format. 182 | # The empty string is equivalent to '%b %d, %Y'. 183 | # 184 | # html_last_updated_fmt = None 185 | 186 | # If true, SmartyPants will be used to convert quotes and dashes to 187 | # typographically correct entities. 188 | # 189 | # html_use_smartypants = True 190 | 191 | # Custom sidebar templates, maps document names to template names. 192 | # 193 | # html_sidebars = {} 194 | 195 | # Additional templates that should be rendered to pages, maps page names to 196 | # template names. 197 | # 198 | # html_additional_pages = {} 199 | 200 | # If false, no module index is generated. 201 | # 202 | # html_domain_indices = True 203 | 204 | # If false, no index is generated. 205 | # 206 | # html_use_index = True 207 | 208 | # If true, the index is split into individual pages for each letter. 209 | # 210 | # html_split_index = False 211 | 212 | # If true, links to the reST sources are added to the pages. 213 | # 214 | # html_show_sourcelink = True 215 | 216 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 217 | # 218 | # html_show_sphinx = True 219 | 220 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 221 | # 222 | # html_show_copyright = True 223 | 224 | # If true, an OpenSearch description file will be output, and all pages will 225 | # contain a tag referring to it. The value of this option must be the 226 | # base URL from which the finished HTML is served. 227 | # 228 | # html_use_opensearch = '' 229 | 230 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 231 | # html_file_suffix = None 232 | 233 | # Language to be used for generating the HTML full-text search index. 234 | # Sphinx supports the following languages: 235 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 236 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' 237 | # 238 | # html_search_language = 'en' 239 | 240 | # A dictionary with options for the search language support, empty by default. 241 | # 'ja' uses this config value. 242 | # 'zh' user can custom change `jieba` dictionary path. 243 | # 244 | # html_search_options = {'type': 'default'} 245 | 246 | # The name of a javascript file (relative to the configuration directory) that 247 | # implements a search results scorer. If empty, the default will be used. 248 | # 249 | # html_search_scorer = 'scorer.js' 250 | 251 | # Output file base name for HTML help builder. 252 | htmlhelp_basename = 'nukecontextsdoc' 253 | 254 | # -- Options for LaTeX output --------------------------------------------- 255 | 256 | latex_elements = { 257 | # The paper size ('letterpaper' or 'a4paper'). 258 | # 259 | # 'papersize': 'letterpaper', 260 | 261 | # The font size ('10pt', '11pt' or '12pt'). 262 | # 263 | # 'pointsize': '10pt', 264 | 265 | # Additional stuff for the LaTeX preamble. 266 | # 267 | # 'preamble': '', 268 | 269 | # Latex figure (float) alignment 270 | # 271 | # 'figure_align': 'htbp', 272 | } 273 | 274 | # Grouping the document tree into LaTeX files. List of tuples 275 | # (source start file, target name, title, 276 | # author, documentclass [howto, manual, or own class]). 277 | latex_documents = [ 278 | (master_doc, 'nukecontexts.tex', u'nukecontexts Documentation', 279 | u'Florian Einfalt', 'manual'), 280 | ] 281 | 282 | # The name of an image file (relative to this directory) to place at the top of 283 | # the title page. 284 | # 285 | # latex_logo = None 286 | 287 | # For "manual" documents, if this is true, then toplevel headings are parts, 288 | # not chapters. 289 | # 290 | # latex_use_parts = False 291 | 292 | # If true, show page references after internal links. 293 | # 294 | # latex_show_pagerefs = False 295 | 296 | # If true, show URL addresses after external links. 297 | # 298 | # latex_show_urls = False 299 | 300 | # Documents to append as an appendix to all manuals. 301 | # 302 | # latex_appendices = [] 303 | 304 | # It false, will not define \strong, \code, itleref, \crossref ... but only 305 | # \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added 306 | # packages. 307 | # 308 | # latex_keep_old_macro_names = True 309 | 310 | # If false, no module index is generated. 311 | # 312 | # latex_domain_indices = True 313 | 314 | 315 | # -- Options for manual page output --------------------------------------- 316 | 317 | # One entry per manual page. List of tuples 318 | # (source start file, name, description, authors, manual section). 319 | man_pages = [ 320 | (master_doc, 'nukecontexts', u'nukecontexts Documentation', 321 | [author], 1) 322 | ] 323 | 324 | # If true, show URL addresses after external links. 325 | # 326 | # man_show_urls = False 327 | 328 | 329 | # -- Options for Texinfo output ------------------------------------------- 330 | 331 | # Grouping the document tree into Texinfo files. List of tuples 332 | # (source start file, target name, title, author, 333 | # dir menu entry, description, category) 334 | texinfo_documents = [ 335 | (master_doc, 'nukecontexts', u'nukecontexts Documentation', 336 | author, 'nukecontexts', 'One line description of project.', 337 | 'Miscellaneous'), 338 | ] 339 | 340 | # Documents to append as an appendix to all manuals. 341 | # 342 | # texinfo_appendices = [] 343 | 344 | # If false, no module index is generated. 345 | # 346 | # texinfo_domain_indices = True 347 | 348 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 349 | # 350 | # texinfo_show_urls = 'footnote' 351 | 352 | # If true, do not generate a @detailmenu in the "Top" node's menu. 353 | # 354 | # texinfo_no_detailmenu = False 355 | 356 | 357 | # Example configuration for intersphinx: refer to the Python standard library. 358 | intersphinx_mapping = {'https://docs.python.org/': None} 359 | -------------------------------------------------------------------------------- /docs/source/getting_started.rst: -------------------------------------------------------------------------------- 1 | Getting Started 2 | =============== 3 | 4 | ``nukecontexts`` is a library of composable context managers for Nuke to 5 | manage the state of complex compositing scripts in code. 6 | 7 | The most common use case for ``nukecontexts`` is automated rendering of 8 | multiple states of a compositing script. For example two different output 9 | formats, jpg and png. 10 | 11 | .. code:: python 12 | 13 | import nuke 14 | import contextlib 15 | from nukecontexts import ctx 16 | 17 | render_node = nuke.toNode('Write1') 18 | with ctx.set_attr(render_node, 'file_type', 'jpeg'): 19 | nuke.execute(render_node.name(), 1, 1, 1) 20 | with ctx.set_attr(render_node, 'file_type', 'png'): 21 | nuke.execute(render_node.name(), 1, 1, 1) 22 | 23 | The power of ``nukecontexts`` comes with composable contexts, using 24 | ``contextlib.nested()``. Arbitrarily complex, varying states of the 25 | compositing script can be defined and used to automatically generate 26 | different results. 27 | 28 | .. code:: python 29 | 30 | merge_node = nuke.toNode('Merge1') 31 | grade_node = nuke.toNode('Grade1') 32 | switch_node = nuke.toNode('Switch1') 33 | 34 | ctx1 = ctx.enable([merge_node, grade_node]) 35 | ctx2 = ctx.set_attr(grade_node, 'white', 2.0) 36 | ctx3 = ctx.set_attr(switch_node, 'which', 0) 37 | ctx4 = ctx.disable(merge_node) 38 | 39 | with contextlib.nested(ctx1, ctx2, ctx3): 40 | """Render with the merge_node and grade_node enabled, the 41 | grade_node's white attribute set to 2.0 and the switch_node's switch 42 | position set to 0.""" 43 | nuke.execute(render_node.name(), 1, 1, 1) 44 | 45 | with contextlib.nested(ctx3, ctx4): 46 | """Render with the switch_node's switch position set to 0 and the 47 | merge node disabled; the grade_node's gain value remains at the 48 | original value.""" 49 | nuke.execute(render_node.name(), 1, 1, 1) 50 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. nukecontexts documentation master file, created by 2 | sphinx-quickstart on Thu Feb 9 15:53:41 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to nukecontexts' documentation! 7 | ======================================= 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | installation 15 | getting_started 16 | advanced_use 17 | api_documentation 18 | 19 | 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | * :ref:`modindex` 26 | * :ref:`search` 27 | 28 | -------------------------------------------------------------------------------- /docs/source/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | To install ``nukecontexts``, type: 5 | 6 | .. code-block:: bash 7 | 8 | $ pip install nukecontexts 9 | 10 | Open Nuke's ``init.py`` file and add: 11 | 12 | .. code-block:: python 13 | 14 | nuke.pluginAddPath('/path/to/your/local/python/site-packages') 15 | -------------------------------------------------------------------------------- /nukecontexts/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import getpass 5 | import logging 6 | import platform 7 | 8 | 9 | def import_nuke(): 10 | try: 11 | import nuke 12 | return nuke 13 | except ImportError as e: 14 | try: 15 | os.environ['NON_PRODUCTION_CONTEXT'] 16 | except KeyError: 17 | raise e 18 | 19 | 20 | TESTING = False 21 | try: 22 | TESTING = os.environ['NON_PRODUCTION_CONTEXT'] 23 | logger = logging.getLogger() 24 | sentry = None 25 | except: 26 | if platform.system() == 'Darwin': 27 | application = r'Nuke\d+\.\d+v\d+.app' 28 | elif platform.system() == 'Windows': 29 | application = r'Nuke\d+\.\d+.exe' 30 | else: 31 | raise RuntimeError('OS {0} is not supported'.format(platform.system())) 32 | 33 | match = re.search(application, sys.executable) 34 | if not match: 35 | raise RuntimeError('Import nukecontexts from within Nuke') 36 | nuke = import_nuke() 37 | 38 | __version__ = '0.2.0' 39 | __all__ = ['ctx'] 40 | 41 | 42 | def create_logger(): 43 | logger = logging.getLogger(__name__) 44 | logger.handlers = [] 45 | handler = logging.StreamHandler(stream=sys.stdout) 46 | formatter = logging.Formatter(fmt='%(asctime)s: %(name)s: ' 47 | '%(levelname)s: %(message)s', 48 | datefmt='%d/%m/%Y %I:%M:%S') 49 | handler.setFormatter(formatter) 50 | logger.addHandler(handler) 51 | logger.setLevel(logging.INFO) 52 | logger.propagate = False 53 | return logger 54 | 55 | 56 | def get_sentry(): 57 | try: 58 | from raven import Client 59 | try: 60 | os.environ['SENTRY_DSN'] 61 | except KeyError: 62 | raise ImportError 63 | client = Client(release=__version__) 64 | client.user_context({'username': getpass.getuser()}) 65 | client.tags_context({ 66 | 'os_version': platform.platform(), 67 | 'nuke_version': nuke.NUKE_VERSION_STRING}) 68 | return client 69 | except ImportError: 70 | return None 71 | 72 | 73 | if not TESTING: 74 | logger = create_logger() 75 | sentry = get_sentry() 76 | -------------------------------------------------------------------------------- /nukecontexts/ctx.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from contextlib import contextmanager 3 | 4 | from tqdm import tqdm 5 | 6 | from nukecontexts import import_nuke, logger 7 | 8 | nuke = import_nuke() 9 | 10 | 11 | class NukeContextError(ValueError): 12 | def __init__(self, message, *args): 13 | self.message = message 14 | super(NukeContextError, self).__init__(message, *args) 15 | 16 | 17 | class Progress(object): 18 | """ 19 | Convenience wrapper class around :func:`tqdm.tqdm` for easy progress bars 20 | 21 | Usage: 22 | 23 | >>> with Progress(iterable) as progress: 24 | >>> for item in progress: 25 | >>> #do something 26 | """ 27 | def __init__(self, iterable, name='nukecontexts', output=sys.stdout): 28 | """ 29 | :param interable: Iterable to generate progress bar for 30 | :type interable: iter 31 | :param name: Progress bar label (default: 'nukecontexts') 32 | :type name: str 33 | :param output: Output stream (default: ``sys.stdout``) 34 | :type output: io.TextIOWrapper or io.StringIO 35 | """ 36 | self.name = name 37 | self.iterable = iterable 38 | self.output = output 39 | 40 | def __enter__(self): 41 | """ 42 | :return: Progress bar 43 | :rtype: tqdm.tqdm 44 | """ 45 | return tqdm(iterable=self.iterable, 46 | desc=self.name, 47 | file=self.output) 48 | 49 | def __exit__(self, exc_type, exc_value, traceback): 50 | pass 51 | 52 | 53 | @contextmanager 54 | def inventory(var): 55 | """ 56 | Given a variable name, create a node inventory on entry and a separate node 57 | inventory on exit and save any new nodes into the newly created variable. 58 | 59 | Beware that the new variable is created in ``__builtins__`` and is 60 | therefore accessible even after the context manager has exited. 61 | 62 | **Use with namespace in mind!** 63 | 64 | :param var: Variable name 65 | :type var: str 66 | """ 67 | import nuke 68 | before = nuke.allNodes() 69 | yield 70 | after = nuke.allNodes() 71 | __builtins__[var] = [node for node in after if node not in before] 72 | 73 | 74 | @contextmanager 75 | def enabled(nodes, log=logger): 76 | """ 77 | Given a list of nodes (:class:`~nuke.Node`), enable on entry and restore 78 | to original value on exit. 79 | 80 | :param nodes: Nodes 81 | :type nodes: list 82 | :param log: Logger 83 | :type log: logging.Logger 84 | """ 85 | with AttributeSetter(nodes, 'disable', False, log=log): 86 | yield 87 | 88 | 89 | @contextmanager 90 | def disabled(nodes, log=logger): 91 | """ 92 | Given a list of nodes (:class:`~nuke.Node`), disable on entry and restore 93 | to original value on exit. 94 | 95 | :param nodes: Nodes 96 | :type nodes: list 97 | :param log: Logger 98 | :type log: logging.Logger 99 | """ 100 | with AttributeSetter(nodes, 'disable', True, log=log): 101 | yield 102 | 103 | 104 | @contextmanager 105 | def set_attr(nodes, attr, value, log=logger): 106 | """ 107 | Given a list of nodes (:class:`~nuke.Node`), set a given ``attr`` to 108 | ``value`` on entry and restore to original value on exit. 109 | 110 | :param nodes: Nodes 111 | :type nodes: list 112 | :param attr: Attribute 113 | :type attr: str 114 | :param value: Value 115 | :type value: str, int, float, bool 116 | :param log: Logger 117 | :type log: logging.Logger 118 | """ 119 | with AttributeSetter(nodes, attr, value, log=log): 120 | yield 121 | 122 | 123 | class AttributeSetter(object): 124 | def __init__(self, nodes, attr, value, log=logger): 125 | """ 126 | Given a list of nodes (:class:`~nuke.Node`), set a given ``attr`` to 127 | ``value`` on entry and restore to original value on exit. 128 | 129 | :param nodes: Nodes 130 | :type nodes: list 131 | :param attr: Attribute 132 | :type attr: str 133 | :param value: Value 134 | :type value: str, int, float, bool 135 | :param log: Logger 136 | :type log: logging.Logger 137 | """ 138 | if not isinstance(nodes, list): 139 | nodes = [nodes] 140 | self.nodes = nodes 141 | self.attr = attr 142 | self.value = value 143 | self.log = log 144 | 145 | def __enter__(self): 146 | self.enter_values = {} 147 | for node in self.nodes: 148 | try: 149 | assert node 150 | except AssertionError: 151 | raise NukeContextError('Invalid node') 152 | try: 153 | self.enter_values[node] = node[self.attr].value() 154 | except NameError as err: 155 | raise NukeContextError('Node \'{0}\': {1}'.format( 156 | node.name(), err.args[0])) 157 | logger.info('Entering context: ({0}|{1}|{2})'.format( 158 | node.name(), self.attr, self.value)) 159 | try: 160 | node[self.attr].setValue(self.value) 161 | except TypeError as err: 162 | raise NukeContextError('Attribute \'{0}\': {1}'.format( 163 | self.attr, err.args[0])) 164 | 165 | def __exit__(self, exc_type, exc_value, traceback): 166 | for node, enter_value in self.enter_values.iteritems(): 167 | logger.info('Restoring context: ({0}|{1}|{2})'.format( 168 | node.name(), self.attr, enter_value)) 169 | node[self.attr].setValue(enter_value) 170 | 171 | 172 | @contextmanager 173 | def multiple_contexts(contexts): 174 | """ 175 | Given a list of contextmanagers, sequentially enter all contextmanagers, 176 | raise :class:`Exception` in case errors occur in contexts. 177 | 178 | **Deprecated**. Use :func:`contextlib.nested(*contexts)`. 179 | 180 | :param contexts: List of contextmanagers 181 | :type contexts: list 182 | """ 183 | msg = ('nukecontexts.ctx.multiple_contexts() is deprecated. ' 184 | 'Use contextlib.nested(*contexts)') 185 | nuke.warning(msg) 186 | 187 | for ctx in contexts: 188 | ctx.__enter__() 189 | 190 | err = None 191 | exc_info = (None, None, None) 192 | try: 193 | yield 194 | except Exception as err: 195 | exc_info = sys.exc_info() 196 | 197 | # exc_info gets passed to each subsequent ctx.__exit__ 198 | # unless one of them suppresses the exception by returning True 199 | for ctx in reversed(contexts): 200 | if ctx.__exit__(*exc_info): 201 | err = False 202 | exc_info = (None, None, None) 203 | if err: 204 | raise err 205 | -------------------------------------------------------------------------------- /run_tests.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | 4 | dir_ = os.path.dirname(__file__) 5 | pytest.main(['{0}/tests/'.format(dir_), 6 | '--cov=nukecontexts', 7 | '--cov-report', 8 | 'term-missing']) 9 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 0 3 | 4 | [egg_info] 5 | tag_build = 6 | tag_date = 0 7 | tag_svn_revision = 0 8 | 9 | [metadata] 10 | license_file = LICENSE 11 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | with open('README.rst', 'r') as f: 4 | readme = f.read() 5 | 6 | requirements = ['tqdm'] 7 | test_requirements = ['pytest', 'pytest-cov', 8 | 'sphinx', 'sphinx_rtd_theme'] 9 | 10 | setup( 11 | name = 'nukecontexts', 12 | version = '0.2.0', 13 | description='A library of composable context managers for Nuke', 14 | long_description=readme, 15 | url='https://github.com/florianeinfalt/nukecontexts', 16 | author='Florian Einfalt', 17 | author_email='info@florianeinfalt.de', 18 | license='Apache 2.0', 19 | packages=find_packages(), 20 | include_package_data=True, 21 | zip_safe=False, 22 | classifiers=[ 23 | 'Development Status :: 3 - Alpha', 24 | 'Intended Audience :: Developers', 25 | 'Natural Language :: English', 26 | 'Topic :: Software Development :: Build Tools', 27 | 'License :: OSI Approved :: Apache Software License', 28 | 'Programming Language :: Python', 29 | 'Programming Language :: Python :: 2.6', 30 | 'Programming Language :: Python :: 2.7', 31 | 'Programming Language :: Python :: Implementation :: CPython', 32 | ], 33 | install_requires=requirements, 34 | tests_require=test_requirements 35 | ) 36 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import nuke 2 | import pytest 3 | 4 | @pytest.fixture(scope='session') 5 | def nuke(): 6 | import nuke 7 | return nuke 8 | 9 | @pytest.fixture(scope='session') 10 | def node(nuke): 11 | return nuke.nodes.Write(name='test_write') 12 | -------------------------------------------------------------------------------- /tests/test_contexts.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from nukecontexts import ctx 3 | 4 | 5 | def test_enabled(node): 6 | node['disable'].setValue(True) 7 | assert node['disable'].value() 8 | with ctx.enabled(node): 9 | assert not node['disable'].value() 10 | assert node['disable'].value() 11 | node['disable'].setValue(False) 12 | 13 | 14 | def test_disabled(node): 15 | assert not node['disable'].value() 16 | with ctx.disabled(node): 17 | assert node['disable'].value() 18 | assert not node['disable'].value() 19 | 20 | 21 | def test_set_attr_errors(node): 22 | # Test invalid node error 23 | with pytest.raises(ctx.NukeContextError): 24 | with ctx.set_attr(None, 'invalid_attr', True): 25 | print 'should not print' 26 | # Test invalid attribute error 27 | with pytest.raises(ctx.NukeContextError): 28 | with ctx.set_attr(node, 'invalid_attr', True): 29 | print 'should not print' 30 | # Test invalid value error 31 | with pytest.raises(ctx.NukeContextError): 32 | with ctx.set_attr(node, 'file_type', 2.0): 33 | print 'should not print' 34 | 35 | 36 | def test_multiple(node): 37 | ctx1 = ctx.enabled(node) 38 | ctx2 = ctx.set_attr(node, 'file_type', 'jpeg') 39 | ctx3 = ctx.set_attr(node, 'channels', 'rgba') 40 | 41 | node['disable'].setValue(True) 42 | node['file_type'].setValue('exr') 43 | 44 | assert node['disable'].value() 45 | assert node['file_type'].value() == 'exr' 46 | assert node['channels'].value() == 'rgb' 47 | 48 | with ctx.multiple_contexts([ctx1, ctx2, ctx3]): 49 | assert not node['disable'].value() 50 | assert node['file_type'].value() == 'jpeg' 51 | assert node['channels'].value() == 'rgba' 52 | 53 | assert node['disable'].value() 54 | assert node['file_type'].value() == 'exr' 55 | assert node['channels'].value() == 'rgb' 56 | 57 | node['disable'].setValue(False) 58 | 59 | 60 | def test_progress(node): 61 | ctx1 = ctx.enabled(node) 62 | iterable = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 63 | 64 | with ctx1, ctx.Progress(iterable) as progress: 65 | for idx, item in enumerate(progress): 66 | assert item == idx + 1 67 | 68 | 69 | def test_inventory(nuke): 70 | with ctx.inventory('new_nodes'): 71 | for i in range(3): 72 | nuke.createNode('Write', inpanel=False) 73 | assert len(new_nodes) == 3 74 | assert any([node for node in new_nodes if node.Class() == 'Write']) 75 | --------------------------------------------------------------------------------