diff --git a/.travis.yml b/.travis.yml index 80ff638e2..67a086b3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ matrix: - docker - language: generic os: osx - env: PYVERS="3.5.7 3.6.8 3.7.3" PY=3 RUN=wheels USE_PANGOFT2=0 + env: PYVERS="3.5.4 3.6.8 3.7.3" PY=3 RUN=wheels USE_PANGOFT2=0 name: "OSX - wheel generation" - language: generic env: PY=3 RUN=app USE_PANGOFT2=0 @@ -111,10 +111,9 @@ install: cp -a Platypus-4.8/Platypus.app/Contents/Resources/MainMenu.nib /usr/local/share/platypus/MainMenu.nib; chmod -R 755 /usr/local/share/platypus; fi; - export PATH=$PATH:$HOME/Library/Python/3.5/bin; - curl -O -L https://www.python.org/ftp/python/3.5.2/python-3.5.2-macosx10.6.pkg; - sudo installer -package python-3.5.2-macosx10.6.pkg -target /; + curl -O -L https://www.python.org/ftp/python/3.5.4/python-3.5.4-macosx10.6.pkg; + sudo installer -package python-3.5.4-macosx10.6.pkg -target /; python3 get-pip.py --user; export CYTHON_INSTALL=$(KIVY_NO_CONSOLELOG=1 python3 -c "from kivy.tools.packaging.cython_cfg import get_cython_versions; print(get_cython_versions()[0])" --config "kivy:log_level:error"); python3 -m pip install -I --user "$CYTHON_INSTALL"; diff --git a/doc/sources/installation/installation-windows.rst b/doc/sources/installation/installation-windows.rst index fd1aad920..73beea48e 100644 --- a/doc/sources/installation/installation-windows.rst +++ b/doc/sources/installation/installation-windows.rst @@ -134,7 +134,9 @@ kivy release (`1.11.0`) and its dependencies. That's it. You should now be able to ``import kivy`` in python or run a basic example if you installed the kivy examples:: - python share\kivy-examples\demo\showcase\main.py + python kivy_venv\share\kivy-examples\demo\showcase\main.py + +Replace `kivy_venv` with the path where python is installed if you didn't use a virtualenv. .. note:: diff --git a/kivy/__init__.py b/kivy/__init__.py index 6e7f36337..009b0a95d 100644 --- a/kivy/__init__.py +++ b/kivy/__init__.py @@ -80,11 +80,11 @@ if sys.version_info[0] == 2: def parse_kivy_version(version): """Parses the kivy version as described in :func:`require` into a 3-tuple - of ([x, y, z], 'rc|a|b|dev', 'N') where N is the tag revision. The last - two elements may be None. + of ([x, y, z], 'rc|a|b|dev|post', 'N') where N is the tag revision. The + last two elements may be None. """ m = re.match( - '^([0-9]+)\\.([0-9]+)\\.([0-9]+?)(rc|a|b|\\.dev)?([0-9]+)?$', + '^([0-9]+)\\.([0-9]+)\\.([0-9]+?)(rc|a|b|\\.dev|\\.post)?([0-9]+)?$', version) if m is None: raise Exception('Revision format must be X.Y.Z[-tag]') @@ -92,6 +92,8 @@ def parse_kivy_version(version): major, minor, micro, tag, tagrev = m.groups() if tag == '.dev': tag = 'dev' + if tag == '.post': + tag = 'post' return [int(major), int(minor), int(micro)], tag, tagrev @@ -113,7 +115,7 @@ def require(version): Y is the minor version Z is the bugfixes revision - The tag is optional, but may be one of '.dev', 'a', 'b', or 'rc'. + The tag is optional, but may be one of '.dev', '.post', 'a', 'b', or 'rc'. The tagrevision is the revision number of the tag. .. warning:: @@ -265,6 +267,45 @@ kivy_config_fn = '' #: Kivy user modules directory kivy_usermodules_dir = '' +# if there are deps, import them so they can do their magic. +import kivy.deps +_packages = [] +for importer, modname, ispkg in pkgutil.iter_modules(kivy.deps.__path__): + if not ispkg: + continue + if modname.startswith('gst'): + _packages.insert(0, (importer, modname, 'kivy.deps')) + else: + _packages.append((importer, modname, 'kivy.deps')) + +try: + import kivy_deps + for importer, modname, ispkg in pkgutil.iter_modules(kivy_deps.__path__): + if not ispkg: + continue + if modname.startswith('gst'): + _packages.insert(0, (importer, modname, 'kivy_deps')) + else: + _packages.append((importer, modname, 'kivy_deps')) +except ImportError: + pass + +_logging_msgs = [] +for importer, modname, package in _packages: + try: + mod = importer.find_module(modname).load_module(modname) + + version = '' + if hasattr(mod, '__version__'): + version = ' {}'.format(mod.__version__) + _logging_msgs.append( + 'deps: Successfully imported "{}.{}"{}'. + format(package, modname, version)) + except ImportError as e: + Logger.warning( + 'deps: Error importing dependency "{}.{}": {}'. + format(package, modname, str(e))) + # Don't go further if we generate documentation if any(name in sys.argv[0] for name in ('sphinx-build', 'autobuild.py')): environ['KIVY_DOC'] = '1' @@ -431,6 +472,9 @@ if not environ.get('KIVY_DOC_INCLUDE'): if platform == 'android': Config.set('input', 'androidtouch', 'android') +for msg in _logging_msgs: + Logger.info(msg) + if RELEASE: Logger.info('Kivy: v%s' % __version__) elif not RELEASE and __hash__ and __date__: @@ -439,44 +483,6 @@ Logger.info('Kivy: Installed at "{}"'.format(__file__)) Logger.info('Python: v{}'.format(sys.version)) Logger.info('Python: Interpreter at "{}"'.format(sys.executable)) -# if there are deps, import them so they can do their magic. -import kivy.deps -_packages = [] -for importer, modname, ispkg in pkgutil.iter_modules(kivy.deps.__path__): - if not ispkg: - continue - if modname.startswith('gst'): - _packages.insert(0, (importer, modname, 'kivy.deps')) - else: - _packages.append((importer, modname, 'kivy.deps')) - -try: - import kivy_deps - for importer, modname, ispkg in pkgutil.iter_modules(kivy_deps.__path__): - if not ispkg: - continue - if modname.startswith('gst'): - _packages.insert(0, (importer, modname, 'kivy_deps')) - else: - _packages.append((importer, modname, 'kivy_deps')) -except ImportError: - pass - -for importer, modname, package in _packages: - try: - mod = importer.find_module(modname).load_module(modname) - - version = '' - if hasattr(mod, '__version__'): - version = ' {}'.format(mod.__version__) - Logger.info( - 'deps: Successfully imported "{}.{}"{}'. - format(package, modname, version)) - except ImportError as e: - Logger.warning( - 'deps: Error importing dependency "{}.{}": {}'. - format(package, modname, str(e))) - from kivy.logger import file_log_handler if file_log_handler is not None: file_log_handler.purge_logs() diff --git a/kivy/properties.pyx b/kivy/properties.pyx index 52c4f9038..5b477b126 100644 --- a/kivy/properties.pyx +++ b/kivy/properties.pyx @@ -699,7 +699,7 @@ cdef class StringProperty(Property): obj.__class__.__name__, self.name)) -cdef inline void observable_list_dispatch(object self): +cdef inline void observable_list_dispatch(object self) except *: cdef Property prop = self.prop obj = self.obj() if obj is not None: @@ -838,7 +838,7 @@ cdef class ListProperty(Property): value = ObservableList(self, obj, value) Property.set(self, obj, value) -cdef inline void observable_dict_dispatch(object self): +cdef inline void observable_dict_dispatch(object self) except *: cdef Property prop = self.prop prop.dispatch(self.obj) diff --git a/kivy/tests/test_uix_carousel.py b/kivy/tests/test_uix_carousel.py new file mode 100644 index 000000000..fd8811b11 --- /dev/null +++ b/kivy/tests/test_uix_carousel.py @@ -0,0 +1,60 @@ +import pytest + + +@pytest.fixture( + scope='session', params=(True, False), ids=lambda v: 'loop=' + str(v)) +def loop(request): + return request.param + + +def test_remove_widget(loop): + from kivy.uix.carousel import Carousel + from kivy.uix.widget import Widget + + c = Carousel(loop=loop) + assert c.index is None + assert c.current_slide is None + assert len(c.children) == 0 + assert len(c.slides) == 0 + + N_SLIDES = 4 + for i in range(N_SLIDES): + c.add_widget(Widget()) + assert c.index == 0 + assert c.current_slide == c.slides[0] + assert len(c.children) == 3 if loop else 2 + assert len(c.slides) == N_SLIDES + + # test issue #6370 + c.index = len(c.slides) - 1 + c.remove_widget(c.slides[0]) + + # remove a slide(smaller index than the current_slide's). + c.index = 1 + old_current_slide = c.current_slide + c.remove_widget(c.slides[c.index - 1]) + assert c.index == 0 + assert c.current_slide is old_current_slide + assert len(c.children) == 2 + assert len(c.slides) == 2 + + # remove a slide(bigger index than the current_slide's). + old_current_slide = c.current_slide + c.remove_widget(c.slides[c.index + 1]) + assert c.index == 0 + assert c.current_slide is old_current_slide + assert len(c.children) == 1 + assert len(c.slides) == 1 + + # remove the current_slide(the last one left). + c.remove_widget(c.current_slide) + assert c.index is None + assert c.current_slide is None + assert len(c.children) == 0 + assert len(c.slides) == 0 + + +if __name__ == "__main__": + pytest.main(args=[ + __file__, + ]) diff --git a/kivy/tools/packaging/pyinstaller_hooks/__init__.py b/kivy/tools/packaging/pyinstaller_hooks/__init__.py index 828bfaf88..e551a1c9d 100644 --- a/kivy/tools/packaging/pyinstaller_hooks/__init__.py +++ b/kivy/tools/packaging/pyinstaller_hooks/__init__.py @@ -334,7 +334,7 @@ def _find_gst_plugin_path(): try: p = subprocess.Popen( ['gst-inspect-1.0', 'coreelements'], - stdout=subprocess.PIPE) + stdout=subprocess.PIPE, universal_newlines=True) except: return [] (stdoutdata, stderrdata) = p.communicate() diff --git a/kivy/tools/packaging/pyinstaller_hooks/pyi_rth_kivy.py b/kivy/tools/packaging/pyinstaller_hooks/pyi_rth_kivy.py index 84bac18e3..0b5e36bef 100644 --- a/kivy/tools/packaging/pyinstaller_hooks/pyi_rth_kivy.py +++ b/kivy/tools/packaging/pyinstaller_hooks/pyi_rth_kivy.py @@ -5,7 +5,8 @@ root = os.path.join(sys._MEIPASS, 'kivy_install') os.environ['KIVY_DATA_DIR'] = os.path.join(root, 'data') os.environ['KIVY_MODULES_DIR'] = os.path.join(root, 'modules') -os.environ['GST_PLUGIN_PATH'] = os.path.join(sys._MEIPASS, 'gst-plugins') +os.environ['GST_PLUGIN_PATH'] = '{}{}{}'.format( + sys._MEIPASS, os.pathsep, os.path.join(sys._MEIPASS, 'gst-plugins')) os.environ['GST_REGISTRY'] = os.path.join(sys._MEIPASS, 'registry.bin') sys.path += [os.path.join(root, '_libs')] diff --git a/kivy/uix/carousel.py b/kivy/uix/carousel.py index bc1ccece1..1e857d8e0 100644 --- a/kivy/uix/carousel.py +++ b/kivy/uix/carousel.py @@ -191,7 +191,7 @@ class Carousel(StencilView): def _curr_slide(self): if len(self.slides): - return self.slides[self.index] + return self.slides[self.index or 0] current_slide = AliasProperty(_curr_slide, bind=('slides', 'index'), @@ -635,9 +635,10 @@ class Carousel(StencilView): return def add_widget(self, widget, index=0, canvas=None): - slide = RelativeLayout(size=self.size, x=self.x - self.width, y=self.y) - slide.add_widget(widget) - super(Carousel, self).add_widget(slide, index, canvas) + container = RelativeLayout( + size=self.size, x=self.x - self.width, y=self.y) + container.add_widget(widget) + super(Carousel, self).add_widget(container, index, canvas) if index != 0: self.slides.insert(index - len(self.slides), widget) else: @@ -648,10 +649,14 @@ class Carousel(StencilView): # added in add_widget(). But it will break if RelativeLayout # implementation change. # if we passed the real widget - if widget in self.slides: - slide = widget.parent - self.slides.remove(widget) - return slide.remove_widget(widget, *args, **kwargs) + slides = self.slides + if widget in slides: + if self.index >= slides.index(widget): + self.index = max(0, self.index - 1) + container = widget.parent + slides.remove(widget) + super(Carousel, self).remove_widget(container) + return container.remove_widget(widget, *args, **kwargs) return super(Carousel, self).remove_widget(widget, *args, **kwargs) def clear_widgets(self): diff --git a/setup.py b/setup.py index d97a2b089..477db6c37 100644 --- a/setup.py +++ b/setup.py @@ -182,6 +182,10 @@ for key in list(c_options.keys()): print('Environ change {0} -> {1}'.format(key, value)) c_options[key] = value +use_embed_signature = environ.get('USE_EMBEDSIGNATURE', '0') == '1' +use_embed_signature = use_embed_signature or bool( + platform not in ('ios', 'android')) + # ----------------------------------------------------------------------------- # We want to be able to install kivy as a wheel without a dependency # on cython, but we also want to use cython where possible as a setup @@ -535,7 +539,7 @@ class CythonExtension(Extension): self.cython_directives = { 'c_string_encoding': 'utf-8', 'profile': 'USE_PROFILE' in environ, - 'embedsignature': 'USE_EMBEDSIGNATURE' in environ} + 'embedsignature': use_embed_signature} # XXX with pip, setuptools is imported before distutils, and change # our pyx to c, then, cythonize doesn't happen. So force again our # sources