diff --git a/.gitignore b/.gitignore index f64623ff7f..4e87106445 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,30 @@ *.orig __pycache__ +*.egg-info *.trace build qtcreator-build *.kdev4 *~ .kateconfig CMakeLists.txt.user* .directory *.autosave *.swp .gdb_history .kdev_include_paths *.config *.creator *.creator.user *.files *.includes .DS_Store *.kate-swap .idea GTAGS GPATH GRTAGS GSYMS BROWSE *.kate-swp /po/ diff --git a/dev-tools/python/dev-requirements.txt b/dev-tools/python/dev-requirements.txt new file mode 100644 index 0000000000..3f32157dd9 --- /dev/null +++ b/dev-tools/python/dev-requirements.txt @@ -0,0 +1,6 @@ +flake8==3.6.0 +pytest==4.0.2 +PyQt5==5.11.3 +PyQt5-sip==4.19.13 + +-e dev-tools/python/krita-mock diff --git a/dev-tools/python/krita-mock/setup.py b/dev-tools/python/krita-mock/setup.py new file mode 100644 index 0000000000..93198f1ded --- /dev/null +++ b/dev-tools/python/krita-mock/setup.py @@ -0,0 +1,8 @@ +from setuptools import setup, find_namespace_packages + +setup( + name="krita", + version="0.1", + packages=find_namespace_packages('src'), + package_dir={'': 'src'}, +) diff --git a/dev-tools/python/krita-mock/src/krita/__init__.py b/dev-tools/python/krita-mock/src/krita/__init__.py new file mode 100644 index 0000000000..ac3fd3a76a --- /dev/null +++ b/dev-tools/python/krita-mock/src/krita/__init__.py @@ -0,0 +1,31 @@ +"""This is a mock krita module for Python unit tests. + +This module returns a mock object for any attribute name and thus +prevents any errors surrounding the krita module in unit tests. This +makes it possible to write unit tests for Krita-independent code +units. + +Caveats: + +Will only work with proper imports: + + import krita + krita.Krita.instance() # no-op on a mock object + +Not with wildcard imports: + + from krita import * + Krita.instance() # error + +(Wildcard imports should be avoided anyway.) + +""" + +import builtins +import sys +from unittest.mock import MagicMock + + +sys.modules['krita'] = MagicMock() + +builtins.i18n = lambda s: s diff --git a/plugins/python/CMakeLists.txt b/plugins/python/CMakeLists.txt index 5a7f2538e8..2624db1435 100644 --- a/plugins/python/CMakeLists.txt +++ b/plugins/python/CMakeLists.txt @@ -1,115 +1,117 @@ # Copyright (C) 2012, 2013 Shaheed Haque # Copyright (C) 2013 Alex Turbov # Copyright (C) 2014-2016 Boudewijn Rempt # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. include(CMakeParseArguments) # # Simple helper function to install plugin and related files # having only a name of the plugin... # (just to reduce syntactic noise when a lot of plugins get installed) # function(install_pykrita_plugin name) set(_options) set(_one_value_args) set(_multi_value_args PATTERNS FILE) cmake_parse_arguments(install_pykrita_plugin "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN}) if(NOT name) message(FATAL_ERROR "Plugin filename is not given") endif() if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py) install(FILES kritapykrita_${name}.desktop DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita) foreach(_f ${name}.py ${name}.ui ${install_pykrita_plugin_FILE}) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_f}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${_f} DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita) endif() endforeach() elseif(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${name}) install(FILES ${name}/kritapykrita_${name}.desktop DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita) install( DIRECTORY ${name} DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita FILES_MATCHING PATTERN "*.py" PATTERN "*.ui" PATTERN "*.txt" PATTERN "*.csv" PATTERN "*.html" PATTERN "__pycache__*" EXCLUDE + PATTERN "tests*" EXCLUDE ) # TODO Is there any way to form a long PATTERN options string # and use it in a single install() call? # NOTE Install specified patterns one-by-one... foreach(_pattern ${install_pykrita_plugin_PATTERNS}) install( DIRECTORY ${name} DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita FILES_MATCHING PATTERN "${_pattern}" PATTERN "__pycache__*" EXCLUDE + PATTERN "tests*" EXCLUDE ) endforeach() else() message(FATAL_ERROR "Do not know what to do with ${name}") endif() endfunction() install_pykrita_plugin(hello) install_pykrita_plugin(assignprofiledialog) install_pykrita_plugin(scripter) install_pykrita_plugin(colorspace) install_pykrita_plugin(documenttools) install_pykrita_plugin(filtermanager) install_pykrita_plugin(exportlayers) #install_pykrita_plugin(highpass) install_pykrita_plugin(tenbrushes) install_pykrita_plugin(tenscripts) install_pykrita_plugin(palette_docker) install_pykrita_plugin(quick_settings_docker) install_pykrita_plugin(lastdocumentsdocker) # install_pykrita_plugin(scriptdocker) install_pykrita_plugin(comics_project_management_tools) install_pykrita_plugin(krita_script_starter) install_pykrita_plugin(plugin_importer) # if(PYTHON_VERSION_MAJOR VERSION_EQUAL 3) # install_pykrita_plugin(cmake_utils) # install_pykrita_plugin(js_utils PATTERNS "*.json") # install_pykrita_plugin(expand PATTERNS "*.expand" "templates/*.tpl") # endif() install( FILES hello/hello.action tenbrushes/tenbrushes.action tenscripts/tenscripts.action plugin_importer/plugin_importer.action DESTINATION ${DATA_INSTALL_DIR}/krita/actions) install( DIRECTORY libkritapykrita DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita FILES_MATCHING PATTERN "*.py" PATTERN "__pycache__*" EXCLUDE ) diff --git a/plugins/python/plugin_importer/tests/__init__.py b/plugins/python/plugin_importer/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/python/plugin_importer/tests/test_plugin_importer.py b/plugins/python/plugin_importer/tests/test_plugin_importer.py new file mode 100644 index 0000000000..4fcf2d4264 --- /dev/null +++ b/plugins/python/plugin_importer/tests/test_plugin_importer.py @@ -0,0 +1,37 @@ +import os +import pytest +from tempfile import TemporaryDirectory +from unittest import TestCase + + +from .. plugin_importer import PluginImporter, PluginReadError + + +class PluginImporterTestCase(TestCase): + + def setUp(self): + self.resources_dir = TemporaryDirectory() + self.plugin_dir = TemporaryDirectory() + + def tearDown(self): + self.resources_dir.cleanup() + self.plugin_dir.cleanup() + + @property + def zip_filename(self): + return os.path.join( + self.resources_dir.name, 'plugin.zip') + + def test_zipfile_doesnt_exist(self): + with pytest.raises(PluginReadError): + PluginImporter(self.zip_filename, + self.resources_dir.name, + lambda x: True) + + def test_zipfile_not_a_zip(self): + with open(self.zip_filename, 'w') as f: + f.write('foo') + with pytest.raises(PluginReadError): + PluginImporter(self.zip_filename, + self.resources_dir.name, + lambda x: True) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000000..b646049a1d --- /dev/null +++ b/setup.cfg @@ -0,0 +1,4 @@ +# Setting for various Python tools (code checks, unit tests etc) + +[flake8] +builtins = i18n,Scripter,Application,Krita